diff --git a/Lib/pathlib.py b/Lib/pathlib.py index e1b07ca9556..01e66a0970b 100644 --- a/Lib/pathlib.py +++ b/Lib/pathlib.py @@ -73,6 +73,10 @@ class _Flavour(object): # parts. This makes the result of parsing e.g. # ("C:", "/", "a") reasonably intuitive. for part in it: + if not part: + continue + if altsep: + part = part.replace(altsep, sep) drv = self.splitroot(part)[0] if drv: break diff --git a/Lib/test/test_pathlib.py b/Lib/test/test_pathlib.py index f4ee5191153..1c53ab78ea4 100644 --- a/Lib/test/test_pathlib.py +++ b/Lib/test/test_pathlib.py @@ -102,31 +102,35 @@ class NTFlavourTest(_BaseFlavourTest, unittest.TestCase): check = self._check_parse_parts # First part is anchored check(['c:'], ('c:', '', ['c:'])) - check(['c:\\'], ('c:', '\\', ['c:\\'])) - check(['\\'], ('', '\\', ['\\'])) + check(['c:/'], ('c:', '\\', ['c:\\'])) + check(['/'], ('', '\\', ['\\'])) check(['c:a'], ('c:', '', ['c:', 'a'])) - check(['c:\\a'], ('c:', '\\', ['c:\\', 'a'])) - check(['\\a'], ('', '\\', ['\\', 'a'])) + check(['c:/a'], ('c:', '\\', ['c:\\', 'a'])) + check(['/a'], ('', '\\', ['\\', 'a'])) # UNC paths - check(['\\\\a\\b'], ('\\\\a\\b', '\\', ['\\\\a\\b\\'])) - check(['\\\\a\\b\\'], ('\\\\a\\b', '\\', ['\\\\a\\b\\'])) - check(['\\\\a\\b\\c'], ('\\\\a\\b', '\\', ['\\\\a\\b\\', 'c'])) + check(['//a/b'], ('\\\\a\\b', '\\', ['\\\\a\\b\\'])) + check(['//a/b/'], ('\\\\a\\b', '\\', ['\\\\a\\b\\'])) + check(['//a/b/c'], ('\\\\a\\b', '\\', ['\\\\a\\b\\', 'c'])) # Second part is anchored, so that the first part is ignored check(['a', 'Z:b', 'c'], ('Z:', '', ['Z:', 'b', 'c'])) - check(['a', 'Z:\\b', 'c'], ('Z:', '\\', ['Z:\\', 'b', 'c'])) - check(['a', '\\b', 'c'], ('', '\\', ['\\', 'b', 'c'])) + check(['a', 'Z:/b', 'c'], ('Z:', '\\', ['Z:\\', 'b', 'c'])) # UNC paths - check(['a', '\\\\b\\c', 'd'], ('\\\\b\\c', '\\', ['\\\\b\\c\\', 'd'])) + check(['a', '//b/c', 'd'], ('\\\\b\\c', '\\', ['\\\\b\\c\\', 'd'])) # Collapsing and stripping excess slashes - check(['a', 'Z:\\\\b\\\\c\\', 'd\\'], ('Z:', '\\', ['Z:\\', 'b', 'c', 'd'])) + check(['a', 'Z://b//c/', 'd/'], ('Z:', '\\', ['Z:\\', 'b', 'c', 'd'])) # UNC paths - check(['a', '\\\\b\\c\\\\', 'd'], ('\\\\b\\c', '\\', ['\\\\b\\c\\', 'd'])) + check(['a', '//b/c//', 'd'], ('\\\\b\\c', '\\', ['\\\\b\\c\\', 'd'])) # Extended paths - check(['\\\\?\\c:\\'], ('\\\\?\\c:', '\\', ['\\\\?\\c:\\'])) - check(['\\\\?\\c:\\a'], ('\\\\?\\c:', '\\', ['\\\\?\\c:\\', 'a'])) + 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(['\\\\?\\UNC\\b\\c'], ('\\\\?\\UNC\\b\\c', '\\', ['\\\\?\\UNC\\b\\c\\'])) - check(['\\\\?\\UNC\\b\\c\\d'], ('\\\\?\\UNC\\b\\c', '\\', ['\\\\?\\UNC\\b\\c\\', 'd'])) + check(['//?/UNC/b/c'], ('\\\\?\\UNC\\b\\c', '\\', ['\\\\?\\UNC\\b\\c\\'])) + check(['//?/UNC/b/c/d'], ('\\\\?\\UNC\\b\\c', '\\', ['\\\\?\\UNC\\b\\c\\', 'd'])) + # Second part has a root but not drive + check(['a', '/b', 'c'], ('', '\\', ['\\', 'b', 'c'])) + check(['Z:/a', '/b', 'c'], ('Z:', '\\', ['Z:\\', 'b', 'c'])) + check(['//?/Z:/a', '/b', 'c'], ('\\\\?\\Z:', '\\', ['\\\\?\\Z:\\', 'b', 'c'])) def test_splitroot(self): f = self.flavour.splitroot diff --git a/Misc/NEWS b/Misc/NEWS index 9051e00b357..d4401514673 100644 --- a/Misc/NEWS +++ b/Misc/NEWS @@ -13,6 +13,9 @@ Core and Builtins Library ------- +- Issue #23146: Fix mishandling of absolute Windows paths with forward + slashes in pathlib. + - Issue #23096: Pickle representation of floats with protocol 0 now is the same for both Python and C implementations.