Visitar URL original
GH-78079: Fix UNC device path root normalization in pathlib by barneygale · Pull Request #102003 · python/cpython · GitHub
Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 8 additions & 3 deletions Lib/pathlib.py
Original file line number Diff line number Diff line change
Expand Up @@ -281,9 +281,14 @@ def _parse_parts(cls, parts):
if altsep:
path = path.replace(altsep, sep)
drv, root, rel = cls._flavour.splitroot(path)
if drv.startswith(sep):
# pathlib assumes that UNC paths always have a root.
root = sep
if not root and drv.startswith(sep) and not drv.endswith(sep):
drv_parts = drv.split(sep)
if len(drv_parts) == 4 and drv_parts[2] not in '?.':
# e.g. //server/share
root = sep
elif len(drv_parts) == 6:
# e.g. //?/unc/server/share
root = sep
Comment thread
barneygale marked this conversation as resolved.
unfiltered_parsed = [drv + root] + rel.split(sep)
parsed = [sys.intern(x) for x in unfiltered_parsed if x and x != '.']
return drv, root, parsed
Expand Down
8 changes: 8 additions & 0 deletions Lib/test/test_ntpath.py
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,7 @@ def test_splitroot(self):

# gh-81790: support device namespace, including UNC drives.
tester('ntpath.splitroot("//?/c:")', ("//?/c:", "", ""))
tester('ntpath.splitroot("//./c:")', ("//./c:", "", ""))
tester('ntpath.splitroot("//?/c:/")', ("//?/c:", "/", ""))
tester('ntpath.splitroot("//?/c:/dir")', ("//?/c:", "/", "dir"))
tester('ntpath.splitroot("//?/UNC")', ("//?/UNC", "", ""))
Expand All @@ -178,8 +179,12 @@ def test_splitroot(self):
tester('ntpath.splitroot("//?/VOLUME{00000000-0000-0000-0000-000000000000}/spam")',
('//?/VOLUME{00000000-0000-0000-0000-000000000000}', '/', 'spam'))
tester('ntpath.splitroot("//?/BootPartition/")', ("//?/BootPartition", "/", ""))
tester('ntpath.splitroot("//./BootPartition/")', ("//./BootPartition", "/", ""))
tester('ntpath.splitroot("//./PhysicalDrive0")', ("//./PhysicalDrive0", "", ""))
tester('ntpath.splitroot("//./nul")', ("//./nul", "", ""))

tester('ntpath.splitroot("\\\\?\\c:")', ("\\\\?\\c:", "", ""))
tester('ntpath.splitroot("\\\\.\\c:")', ("\\\\.\\c:", "", ""))
tester('ntpath.splitroot("\\\\?\\c:\\")', ("\\\\?\\c:", "\\", ""))
tester('ntpath.splitroot("\\\\?\\c:\\dir")', ("\\\\?\\c:", "\\", "dir"))
tester('ntpath.splitroot("\\\\?\\UNC")', ("\\\\?\\UNC", "", ""))
Expand All @@ -192,6 +197,9 @@ def test_splitroot(self):
tester('ntpath.splitroot("\\\\?\\VOLUME{00000000-0000-0000-0000-000000000000}\\spam")',
('\\\\?\\VOLUME{00000000-0000-0000-0000-000000000000}', '\\', 'spam'))
tester('ntpath.splitroot("\\\\?\\BootPartition\\")', ("\\\\?\\BootPartition", "\\", ""))
tester('ntpath.splitroot("\\\\.\\BootPartition\\")', ("\\\\.\\BootPartition", "\\", ""))
tester('ntpath.splitroot("\\\\.\\PhysicalDrive0")', ("\\\\.\\PhysicalDrive0", "", ""))
tester('ntpath.splitroot("\\\\.\\nul")', ("\\\\.\\nul", "", ""))

# gh-96290: support partial/invalid UNC drives
tester('ntpath.splitroot("//")', ("//", "", "")) # empty server & missing share
Expand Down
17 changes: 17 additions & 0 deletions Lib/test/test_pathlib.py
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,9 @@ def test_parse_parts(self):
check(['c:/a'], ('c:', '\\', ['c:\\', 'a']))
check(['/a'], ('', '\\', ['\\', 'a']))
# UNC paths.
check(['//'], ('\\\\', '', ['\\\\']))
check(['//a'], ('\\\\a', '', ['\\\\a']))
check(['//a/'], ('\\\\a\\', '', ['\\\\a\\']))
check(['//a/b'], ('\\\\a\\b', '\\', ['\\\\a\\b\\']))
check(['//a/b/'], ('\\\\a\\b', '\\', ['\\\\a\\b\\']))
check(['//a/b/c'], ('\\\\a\\b', '\\', ['\\\\a\\b\\', 'c']))
Expand All @@ -108,12 +111,26 @@ def test_parse_parts(self):
# UNC paths.
check(['a', '//b/c//', 'd'], ('\\\\b\\c', '\\', ['\\\\b\\c\\', 'd']))
# Extended paths.
check(['//./c:'], ('\\\\.\\c:', '', ['\\\\.\\c:']))
check(['//?/c:/'], ('\\\\?\\c:', '\\', ['\\\\?\\c:\\']))
check(['//?/c:/a'], ('\\\\?\\c:', '\\', ['\\\\?\\c:\\', 'a']))
check(['//?/c:/a', '/b'], ('\\\\?\\c:', '\\', ['\\\\?\\c:\\', 'b']))
# Extended UNC paths (format is "\\?\UNC\server\share").
check(['//?'], ('\\\\?', '', ['\\\\?']))
check(['//?/'], ('\\\\?\\', '', ['\\\\?\\']))
check(['//?/UNC'], ('\\\\?\\UNC', '', ['\\\\?\\UNC']))
check(['//?/UNC/'], ('\\\\?\\UNC\\', '', ['\\\\?\\UNC\\']))
check(['//?/UNC/b'], ('\\\\?\\UNC\\b', '', ['\\\\?\\UNC\\b']))
check(['//?/UNC/b/'], ('\\\\?\\UNC\\b\\', '', ['\\\\?\\UNC\\b\\']))
check(['//?/UNC/b/c'], ('\\\\?\\UNC\\b\\c', '\\', ['\\\\?\\UNC\\b\\c\\']))
check(['//?/UNC/b/c/'], ('\\\\?\\UNC\\b\\c', '\\', ['\\\\?\\UNC\\b\\c\\']))
check(['//?/UNC/b/c/d'], ('\\\\?\\UNC\\b\\c', '\\', ['\\\\?\\UNC\\b\\c\\', 'd']))
# UNC device paths
check(['//./BootPartition/'], ('\\\\.\\BootPartition', '\\', ['\\\\.\\BootPartition\\']))
check(['//?/BootPartition/'], ('\\\\?\\BootPartition', '\\', ['\\\\?\\BootPartition\\']))
check(['//./PhysicalDrive0'], ('\\\\.\\PhysicalDrive0', '', ['\\\\.\\PhysicalDrive0']))
check(['//?/Volume{}/'], ('\\\\?\\Volume{}', '\\', ['\\\\?\\Volume{}\\']))
check(['//./nul'], ('\\\\.\\nul', '', ['\\\\.\\nul']))
# Second part has a root but not drive.
check(['a', '/b', 'c'], ('', '\\', ['\\', 'b', 'c']))
check(['Z:/a', '/b', 'c'], ('Z:', '\\', ['Z:\\', 'b', 'c']))
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
Fix incorrect normalization of UNC device path roots, and partial UNC share
path roots, in :class:`pathlib.PurePath`. Pathlib no longer appends a
trailing slash to such paths.