From 3641c014a825a00c8dc6241e33fe069e58320e4f Mon Sep 17 00:00:00 2001 From: Dustin Ingram Date: Thu, 13 Feb 2025 16:43:29 +0000 Subject: [PATCH 1/3] Ensure version in wheel filename is normalized --- src/packaging/utils.py | 5 +++++ tests/test_utils.py | 18 ++++++++++++++++++ 2 files changed, 23 insertions(+) diff --git a/src/packaging/utils.py b/src/packaging/utils.py index 23450953..a3364d17 100644 --- a/src/packaging/utils.py +++ b/src/packaging/utils.py @@ -120,6 +120,11 @@ def parse_wheel_filename( f"Invalid wheel filename (invalid version): {filename!r}" ) from e + if str(version) != parts[1]: + raise InvalidWheelFilename( + f"Invalid wheel filename (non-normalized version): {filename!r}" + ) + if dashes == 5: build_part = parts[2] build_match = _build_tag_regex.match(build_part) diff --git a/tests/test_utils.py b/tests/test_utils.py index a8cc8d71..fe3b3bc1 100644 --- a/tests/test_utils.py +++ b/tests/test_utils.py @@ -154,6 +154,24 @@ def test_parse_wheel_filename(filename, name, version, build, tags): # Build number doesn't start with a digit (`abc`) ("foo-1.0-abc-py3-none-any.whl"), ("foo-1.0-200-py3-none-any-junk.whl"), # Too many dashes (`-junk`) + ("foo-01.0.0-py3-none-any.whl"), # Non-normalized version + ("foo-1.1RC1-py3-none-any.whl"), # Non-normalized version + ("foo-1.1.a1-py3-none-any.whl"), # Non-normalized version + ("foo-1.1_a1-py3-none-any.whl"), # Non-normalized version + ("foo-1.0a.1-py3-none-any.whl"), # Non-normalized version + ("foo-1.1alpha1-py3-none-any.whl"), # Non-normalized version + ("foo-1.1beta2-py3-none-any.whl"), # Non-normalized version + ("foo-1.1c2-py3-none-any.whl"), # Non-normalized version + ("foo-1.2a-py3-none-any.whl"), # Non-normalized version + ("foo-1.2_post2-py3-none-any.whl"), # Non-normalized version + ("foo-1.2post2-py3-none-any.whl"), # Non-normalized version + ("foo-1.2.post.2-py3-none-any.whl"), # Non-normalized version + ("foo-1.0.r4-py3-none-any.whl"), # Non-normalized version + ("foo-1.2.post-py3-none-any.whl"), # Non-normalized version + ("foo-1.2dev2-py3-none-any.whl"), # Non-normalized version + ("foo-1.2.dev-py3-none-any.whl"), # Non-normalized version + ("foo-1.0+ubuntu_1-py3-none-any.whl"), # Non-normalized version + ("foo-v1.0-py3-none-any.whl"), # Non-normalized version ], ) def test_parse_wheel_invalid_filename(filename): From c62a6f3ccb1e5c50228a0ea07b93123475812991 Mon Sep 17 00:00:00 2001 From: Dustin Ingram Date: Thu, 13 Feb 2025 16:44:22 +0000 Subject: [PATCH 2/3] Homogenize error messages --- src/packaging/utils.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/packaging/utils.py b/src/packaging/utils.py index a3364d17..608f9b95 100644 --- a/src/packaging/utils.py +++ b/src/packaging/utils.py @@ -110,7 +110,9 @@ def parse_wheel_filename( name_part = parts[0] # See PEP 427 for the rules on escaping the project name. if "__" in name_part or re.match(r"^[\w\d._]*$", name_part, re.UNICODE) is None: - raise InvalidWheelFilename(f"Invalid project name: {filename!r}") + raise InvalidWheelFilename( + f"Invalid wheel filename (invalid project name {name_part!r}): {filename!r}" + ) name = canonicalize_name(name_part) try: @@ -130,7 +132,8 @@ def parse_wheel_filename( build_match = _build_tag_regex.match(build_part) if build_match is None: raise InvalidWheelFilename( - f"Invalid build number: {build_part} in {filename!r}" + f"Invalid wheel filename (invalid build number {build_part!r}): " + f"{filename!r}" ) build = cast(BuildTag, (int(build_match.group(1)), build_match.group(2))) else: From e086a8ce86fe3a6787652dae96d03c97436ef362 Mon Sep 17 00:00:00 2001 From: Dustin Ingram Date: Thu, 13 Feb 2025 16:45:07 +0000 Subject: [PATCH 3/3] Assert on error message in test --- tests/test_utils.py | 59 +++++++++++++++++++++++---------------------- 1 file changed, 30 insertions(+), 29 deletions(-) diff --git a/tests/test_utils.py b/tests/test_utils.py index fe3b3bc1..d6ae2cc2 100644 --- a/tests/test_utils.py +++ b/tests/test_utils.py @@ -144,40 +144,41 @@ def test_parse_wheel_filename(filename, name, version, build, tags): @pytest.mark.parametrize( - ("filename"), + ("filename", "expected"), [ - ("foo-1.0.whl"), # Missing tags - ("foo-1.0-py3-none-any.wheel"), # Incorrect file extension (`.wheel`) - ("foo__bar-1.0-py3-none-any.whl"), # Invalid name (`__`) - ("foo#bar-1.0-py3-none-any.whl"), # Invalid name (`#`) - ("foobar-1.x-py3-none-any.whl"), # Invalid version (`1.x`) - # Build number doesn't start with a digit (`abc`) - ("foo-1.0-abc-py3-none-any.whl"), - ("foo-1.0-200-py3-none-any-junk.whl"), # Too many dashes (`-junk`) - ("foo-01.0.0-py3-none-any.whl"), # Non-normalized version - ("foo-1.1RC1-py3-none-any.whl"), # Non-normalized version - ("foo-1.1.a1-py3-none-any.whl"), # Non-normalized version - ("foo-1.1_a1-py3-none-any.whl"), # Non-normalized version - ("foo-1.0a.1-py3-none-any.whl"), # Non-normalized version - ("foo-1.1alpha1-py3-none-any.whl"), # Non-normalized version - ("foo-1.1beta2-py3-none-any.whl"), # Non-normalized version - ("foo-1.1c2-py3-none-any.whl"), # Non-normalized version - ("foo-1.2a-py3-none-any.whl"), # Non-normalized version - ("foo-1.2_post2-py3-none-any.whl"), # Non-normalized version - ("foo-1.2post2-py3-none-any.whl"), # Non-normalized version - ("foo-1.2.post.2-py3-none-any.whl"), # Non-normalized version - ("foo-1.0.r4-py3-none-any.whl"), # Non-normalized version - ("foo-1.2.post-py3-none-any.whl"), # Non-normalized version - ("foo-1.2dev2-py3-none-any.whl"), # Non-normalized version - ("foo-1.2.dev-py3-none-any.whl"), # Non-normalized version - ("foo-1.0+ubuntu_1-py3-none-any.whl"), # Non-normalized version - ("foo-v1.0-py3-none-any.whl"), # Non-normalized version + ("foo-1.0.whl", "wrong number of parts"), + ("foo-1.0-py3-none-any.wheel", "extension must be '.whl'"), + ("foo__bar-1.0-py3-none-any.whl", "invalid project name"), + ("foo#bar-1.0-py3-none-any.whl", "invalid project name"), + ("foobar-1.x-py3-none-any.whl", "invalid version"), + ("foo-1.0-abc-py3-none-any.whl", "invalid build number"), + ("foo-1.0-200-py3-none-any-junk.whl", "wrong number of parts"), + ("foo-01.0.0-py3-none-any.whl", "non-normalized version"), + ("foo-1.1RC1-py3-none-any.whl", "non-normalized version"), + ("foo-1.1.a1-py3-none-any.whl", "non-normalized version"), + ("foo-1.1_a1-py3-none-any.whl", "non-normalized version"), + ("foo-1.0a.1-py3-none-any.whl", "non-normalized version"), + ("foo-1.1alpha1-py3-none-any.whl", "non-normalized version"), + ("foo-1.1beta2-py3-none-any.whl", "non-normalized version"), + ("foo-1.1c2-py3-none-any.whl", "non-normalized version"), + ("foo-1.2a-py3-none-any.whl", "non-normalized version"), + ("foo-1.2_post2-py3-none-any.whl", "non-normalized version"), + ("foo-1.2post2-py3-none-any.whl", "non-normalized version"), + ("foo-1.2.post.2-py3-none-any.whl", "non-normalized version"), + ("foo-1.0.r4-py3-none-any.whl", "non-normalized version"), + ("foo-1.2.post-py3-none-any.whl", "non-normalized version"), + ("foo-1.2dev2-py3-none-any.whl", "non-normalized version"), + ("foo-1.2.dev-py3-none-any.whl", "non-normalized version"), + ("foo-1.0+ubuntu_1-py3-none-any.whl", "non-normalized version"), + ("foo-v1.0-py3-none-any.whl", "non-normalized version"), ], ) -def test_parse_wheel_invalid_filename(filename): - with pytest.raises(InvalidWheelFilename): +def test_parse_wheel_invalid_filename(filename, expected): + with pytest.raises(InvalidWheelFilename) as e: parse_wheel_filename(filename) + assert expected in str(e.value) + @pytest.mark.parametrize( ("filename", "name", "version"),