From de09c3217aa54672806e84f1e3a09c22974f6d0c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0ar=C5=ABnas=20Nejus?= Date: Sun, 11 May 2025 01:35:47 +0100 Subject: [PATCH] Do not 'legalize' paths by removing everything following a dot TIL that `with_suffix` does not simply append the suffix to the filename - it instead replaces the old/current suffix. Or whatever seems to look like a suffix, in our case, unfortunately... --- beets/util/__init__.py | 2 +- docs/changelog.rst | 5 +++++ test/test_util.py | 18 +++++++++++------- 3 files changed, 17 insertions(+), 8 deletions(-) diff --git a/beets/util/__init__.py b/beets/util/__init__.py index 6d0ce0f88..e17de1f51 100644 --- a/beets/util/__init__.py +++ b/beets/util/__init__.py @@ -715,7 +715,7 @@ def truncate_path(str_path: str) -> str: path = Path(str_path) parent_parts = [truncate_str(p, max_length) for p in path.parts[:-1]] stem = truncate_str(path.stem, max_length - len(path.suffix)) - return str(Path(*parent_parts, stem).with_suffix(path.suffix)) + return str(Path(*parent_parts, stem)) + path.suffix def _legalize_stage( diff --git a/docs/changelog.rst b/docs/changelog.rst index 9dd129712..ab0b9519d 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -10,6 +10,11 @@ New features: Bug fixes: +* :doc:`/reference/pathformat`: Fixed a regression where path legalization + incorrectly removed parts of user-configured path formats that followed a dot + (**.**). + :bug:`5771` + For packagers: Other changes: diff --git a/test/test_util.py b/test/test_util.py index 3a5e55c49..6b795b957 100644 --- a/test/test_util.py +++ b/test/test_util.py @@ -171,6 +171,8 @@ class PathConversionTest(BeetsTestCase): class TestPathLegalization: + _p = pytest.param + @pytest.fixture(autouse=True) def _patch_max_filename_length(self, monkeypatch): monkeypatch.setattr("beets.util.get_max_filename_length", lambda: 5) @@ -178,20 +180,22 @@ class TestPathLegalization: @pytest.mark.parametrize( "path, expected", [ - ("abcdeX/fgh", "abcde/fgh"), - ("abcde/fXX.ext", "abcde/f.ext"), - ("a🎹/a.ext", "a🎹/a.ext"), - ("ab🎹/a.ext", "ab/a.ext"), + _p("abcdeX/fgh", "abcde/fgh", id="truncate-parent-dir"), + _p("abcde/fXX.ext", "abcde/f.ext", id="truncate-filename"), + # note that 🎹 is 4 bytes long: + # >>> "🎹".encode("utf-8") + # b'\xf0\x9f\x8e\xb9' + _p("a🎹/a.ext", "a🎹/a.ext", id="unicode-fit"), + _p("ab🎹/a.ext", "ab/a.ext", id="unicode-truncate-fully-one-byte-over-limit"), + _p("f.a.e", "f.a.e", id="persist-dot-in-filename"), # see #5771 ], - ) + ) # fmt: skip def test_truncate(self, path, expected): path = path.replace("/", os.path.sep) expected = expected.replace("/", os.path.sep) assert util.truncate_path(path) == expected - _p = pytest.param - @pytest.mark.parametrize( "replacements, expected_path, expected_truncated", [ # [ repl before truncation, repl after truncation ]