]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
gh-94909: fix joining of absolute and relative Windows paths in pathlib (GH-95450)
authorBarney Gale <barney.gale@gmail.com>
Fri, 12 Aug 2022 21:23:41 +0000 (22:23 +0100)
committerGitHub <noreply@github.com>
Fri, 12 Aug 2022 21:23:41 +0000 (14:23 -0700)
Have pathlib use `os.path.join()` to join arguments to the `PurePath` initialiser, which fixes a minor bug when handling relative paths with drives.

Previously:

```python
>>> from pathlib import PureWindowsPath
>>> a = 'C:/a/b'
>>> b = 'C:x/y'
>>> PureWindowsPath(a, b)
PureWindowsPath('C:x/y')
```

Now:

```python
>>> PureWindowsPath(a, b)
PureWindowsPath('C:/a/b/x/y')
```

Lib/pathlib.py
Lib/test/test_pathlib.py
Misc/NEWS.d/next/Library/2022-07-29-20-58-37.gh-issue-94909.YjMusj.rst [new file with mode: 0644]

index 54da1c8a625398f69e29d750a5c2cf16a2079d2a..0ea621cc601037f3886256a9e9da9dfc5c93bc04 100644 (file)
@@ -61,41 +61,16 @@ class _Flavour(object):
         self.join = self.sep.join
 
     def parse_parts(self, parts):
-        parsed = []
+        if not parts:
+            return '', '', []
         sep = self.sep
         altsep = self.altsep
-        drv = root = ''
-        it = reversed(parts)
-        for part in it:
-            if not part:
-                continue
-            if altsep:
-                part = part.replace(altsep, sep)
-            drv, root, rel = self.splitroot(part)
-            if sep in rel:
-                for x in reversed(rel.split(sep)):
-                    if x and x != '.':
-                        parsed.append(sys.intern(x))
-            else:
-                if rel and rel != '.':
-                    parsed.append(sys.intern(rel))
-            if drv or root:
-                if not drv:
-                    # If no drive is present, try to find one in the previous
-                    # 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
-                break
-        if drv or root:
-            parsed.append(drv + root)
-        parsed.reverse()
+        path = self.pathmod.join(*parts)
+        if altsep:
+            path = path.replace(altsep, sep)
+        drv, root, rel = self.splitroot(path)
+        unfiltered_parsed = [drv + root] + rel.split(sep)
+        parsed = [sys.intern(x) for x in unfiltered_parsed if x and x != '.']
         return drv, root, parsed
 
     def join_parsed_parts(self, drv, root, parts, drv2, root2, parts2):
index 668af8030c0c6a4dbb2ad18c9e8d3d0c91ae3c4d..f324177ff8557373b2481e0d53aaacf0a26ccccd 100644 (file)
@@ -136,6 +136,10 @@ class NTFlavourTest(_BaseFlavourTest, unittest.TestCase):
         check(['a', '/b', 'c'],         ('', '\\', ['\\', 'b', 'c']))
         check(['Z:/a', '/b', 'c'],      ('Z:', '\\', ['Z:\\', 'b', 'c']))
         check(['//?/Z:/a', '/b', 'c'],  ('\\\\?\\Z:', '\\', ['\\\\?\\Z:\\', 'b', 'c']))
+        # Joining with the same drive => the first path is appended to if
+        # the second path is relative.
+        check(['c:/a/b', 'c:x/y'], ('c:', '\\', ['c:\\', 'a', 'b', 'x', 'y']))
+        check(['c:/a/b', 'c:/x/y'], ('c:', '\\', ['c:\\', 'x', 'y']))
 
     def test_splitroot(self):
         f = self.flavour.splitroot
diff --git a/Misc/NEWS.d/next/Library/2022-07-29-20-58-37.gh-issue-94909.YjMusj.rst b/Misc/NEWS.d/next/Library/2022-07-29-20-58-37.gh-issue-94909.YjMusj.rst
new file mode 100644 (file)
index 0000000..b6d8538
--- /dev/null
@@ -0,0 +1,2 @@
+Fix incorrect joining of relative Windows paths with drives in
+:class:`pathlib.PurePath` initializer.