Skip to content

Commit 006fa42

Browse files
committed
gh-149137: Fix PackagePath to normalize backslashes from Windows RECORD files
Per the packaging spec, backslashes are valid path separators on Windows in RECORD files. However, PackagePath extends PurePosixPath which treats backslashes as literal characters rather than separators, causing .name and .parts to return incorrect values (e.g., the full path string instead of just the filename). Fix by overriding __init__ in PackagePath to replace backslashes with forward slashes before passing to PurePosixPath.
1 parent 4ae1a26 commit 006fa42

3 files changed

Lines changed: 35 additions & 0 deletions

File tree

Lib/importlib/metadata/__init__.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -377,6 +377,16 @@ class PackagePath(pathlib.PurePosixPath):
377377
size: int
378378
dist: Distribution
379379

380+
def __init__(self, *args):
381+
# Normalize Windows backslashes to forward slashes. Per the packaging
382+
# spec, backslashes are valid path separators in RECORD files on Windows,
383+
# but PurePosixPath treats them as literal characters, not separators.
384+
normalized = tuple(
385+
arg.replace('\\', '/') if isinstance(arg, str) else arg
386+
for arg in args
387+
)
388+
super().__init__(*normalized)
389+
380390
def read_text(self, encoding: str = 'utf-8') -> str:
381391
return self.locate().read_text(encoding=encoding)
382392

Lib/test/test_importlib/metadata/test_main.py

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
EntryPoint,
1515
MetadataNotFound,
1616
PackageNotFoundError,
17+
PackagePath,
1718
_unique,
1819
distributions,
1920
entry_points,
@@ -491,3 +492,21 @@ def test_origin(self):
491492
dist = Distribution.from_name('distinfo-pkg')
492493
assert dist.origin.url.endswith('.whl')
493494
assert dist.origin.archive_info.hashes.sha256
495+
496+
497+
class PackagePathTests(unittest.TestCase):
498+
def test_backslash_in_name(self):
499+
# Windows RECORD files may use backslashes as path separators per
500+
# the packaging spec; PackagePath must normalize them so that
501+
# .name/.parts/.parent behave correctly.
502+
p = PackagePath('dist_info-1.0.dist-info\\METADATA')
503+
self.assertEqual(p.name, 'METADATA')
504+
505+
def test_backslash_parts(self):
506+
p = PackagePath('a\\b\\c.py')
507+
self.assertEqual(p.parts, ('a', 'b', 'c.py'))
508+
509+
def test_forward_slash_unchanged(self):
510+
p = PackagePath('dist_info-1.0.dist-info/METADATA')
511+
self.assertEqual(p.name, 'METADATA')
512+
self.assertEqual(p.parts, ('dist_info-1.0.dist-info', 'METADATA'))
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
Fix :class:`importlib.metadata.PackagePath` to normalize Windows backslash
2+
path separators from RECORD files to forward slashes. Per the packaging
3+
specification, backslashes are valid separators on Windows, but
4+
:class:`~pathlib.PurePosixPath` treats them as literal characters, causing
5+
:attr:`~pathlib.PurePath.name` and :attr:`~pathlib.PurePath.parts` to return
6+
incorrect values.

0 commit comments

Comments
 (0)