diff --git a/Lib/importlib/metadata/__init__.py b/Lib/importlib/metadata/__init__.py index 32f4b7d2d6e08b..a6b6ec2584cc40 100644 --- a/Lib/importlib/metadata/__init__.py +++ b/Lib/importlib/metadata/__init__.py @@ -377,6 +377,16 @@ class PackagePath(pathlib.PurePosixPath): size: int dist: Distribution + def __init__(self, *args): + # Normalize Windows backslashes to forward slashes. Per the packaging + # spec, backslashes are valid path separators in RECORD files on Windows, + # but PurePosixPath treats them as literal characters, not separators. + normalized = tuple( + arg.replace('\\', '/') if isinstance(arg, str) else arg + for arg in args + ) + super().__init__(*normalized) + def read_text(self, encoding: str = 'utf-8') -> str: return self.locate().read_text(encoding=encoding) diff --git a/Lib/test/test_importlib/metadata/test_main.py b/Lib/test/test_importlib/metadata/test_main.py index aae052160d9763..6554df8fdd424f 100644 --- a/Lib/test/test_importlib/metadata/test_main.py +++ b/Lib/test/test_importlib/metadata/test_main.py @@ -14,6 +14,7 @@ EntryPoint, MetadataNotFound, PackageNotFoundError, + PackagePath, _unique, distributions, entry_points, @@ -491,3 +492,21 @@ def test_origin(self): dist = Distribution.from_name('distinfo-pkg') assert dist.origin.url.endswith('.whl') assert dist.origin.archive_info.hashes.sha256 + + +class PackagePathTests(unittest.TestCase): + def test_backslash_in_name(self): + # Windows RECORD files may use backslashes as path separators per + # the packaging spec; PackagePath must normalize them so that + # .name/.parts/.parent behave correctly. + p = PackagePath('dist_info-1.0.dist-info\\METADATA') + self.assertEqual(p.name, 'METADATA') + + def test_backslash_parts(self): + p = PackagePath('a\\b\\c.py') + self.assertEqual(p.parts, ('a', 'b', 'c.py')) + + def test_forward_slash_unchanged(self): + p = PackagePath('dist_info-1.0.dist-info/METADATA') + self.assertEqual(p.name, 'METADATA') + self.assertEqual(p.parts, ('dist_info-1.0.dist-info', 'METADATA')) diff --git a/Misc/NEWS.d/next/Library/2026-05-08-11-59-00.gh-issue-149137.Bp3xQz.rst b/Misc/NEWS.d/next/Library/2026-05-08-11-59-00.gh-issue-149137.Bp3xQz.rst new file mode 100644 index 00000000000000..87c84594544a05 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2026-05-08-11-59-00.gh-issue-149137.Bp3xQz.rst @@ -0,0 +1,6 @@ +Fix :class:`importlib.metadata.PackagePath` to normalize Windows backslash +path separators from RECORD files to forward slashes. Per the packaging +specification, backslashes are valid separators on Windows, but +:class:`~pathlib.PurePosixPath` treats them as literal characters, causing +:attr:`~pathlib.PurePath.name` and :attr:`~pathlib.PurePath.parts` to return +incorrect values.