Commits

Antoine Pitrou committed 1074c05

Backport 763f4416c4fd

Comments (0)

Files changed (3)

 Version 1.0
 ^^^^^^^^^^^
 
+- Python issue #19918: Fix PurePath.relative_to() under Windows.
 - Python issue #19921: When Path.mkdir() is called with parents=True, any
   missing parent is created with the default permissions, ignoring the mode
   argument (mimicking the POSIX "mkdir -p" command).
         parts = self._parts
         drv = self._drv
         root = self._root
-        if drv or root:
-            if root:
-                abs_parts = [drv, root] + parts[1:]
-            else:
-                abs_parts = [drv] + parts[1:]
+        if root:
+            abs_parts = [drv, root] + parts[1:]
         else:
             abs_parts = parts
         to_drv, to_root, to_parts = self._parse_args(other)
-        if to_drv or to_root:
-            if to_root:
-                to_abs_parts = [to_drv, to_root] + to_parts[1:]
-            else:
-                to_abs_parts = [to_drv] + to_parts[1:]
+        if to_root:
+            to_abs_parts = [to_drv, to_root] + to_parts[1:]
         else:
             to_abs_parts = to_parts
         n = len(to_abs_parts)
-        if n == 0 and (drv or root) or abs_parts[:n] != to_abs_parts:
+        cf = self._flavour.casefold_parts
+        if (root or drv) if n == 0 else cf(abs_parts[:n]) != cf(to_abs_parts):
             formatted = self._format_parsed_parts(to_drv, to_root, to_parts)
             raise ValueError("{!r} does not start with {!r}"
                              .format(str(self), str(formatted)))
-        return self._from_parsed_parts('', '', abs_parts[n:])
+        return self._from_parsed_parts('', root if n == 1 else '',
+                                       abs_parts[n:])
 
     @property
     def parts(self):
         P = self.cls
         p = P('a/b')
         self.assertRaises(TypeError, p.relative_to)
+        if sys.version_info > (3,):
+            self.assertRaises(TypeError, p.relative_to, b'a')
         self.assertEqual(p.relative_to(P()), P('a/b'))
+        self.assertEqual(p.relative_to(''), P('a/b'))
         self.assertEqual(p.relative_to(P('a')), P('b'))
+        self.assertEqual(p.relative_to('a'), P('b'))
+        self.assertEqual(p.relative_to('a/'), P('b'))
         self.assertEqual(p.relative_to(P('a/b')), P())
+        self.assertEqual(p.relative_to('a/b'), P())
         # With several args
         self.assertEqual(p.relative_to('a', 'b'), P())
         # Unrelated paths
         self.assertRaises(ValueError, p.relative_to, P('/a'))
         p = P('/a/b')
         self.assertEqual(p.relative_to(P('/')), P('a/b'))
+        self.assertEqual(p.relative_to('/'), P('a/b'))
         self.assertEqual(p.relative_to(P('/a')), P('b'))
+        self.assertEqual(p.relative_to('/a'), P('b'))
+        self.assertEqual(p.relative_to('/a/'), P('b'))
         self.assertEqual(p.relative_to(P('/a/b')), P())
+        self.assertEqual(p.relative_to('/a/b'), P())
         # Unrelated paths
         self.assertRaises(ValueError, p.relative_to, P('/c'))
         self.assertRaises(ValueError, p.relative_to, P('/a/b/c'))
         self.assertRaises(ValueError, p.relative_to, P('/a/c'))
         self.assertRaises(ValueError, p.relative_to, P())
+        self.assertRaises(ValueError, p.relative_to, '')
         self.assertRaises(ValueError, p.relative_to, P('a'))
 
     def test_pickling_common(self):
 
     def test_relative_to(self):
         P = self.cls
-        p = P('c:a/b')
-        self.assertEqual(p.relative_to(P('c:')), P('a/b'))
-        self.assertEqual(p.relative_to(P('c:a')), P('b'))
-        self.assertEqual(p.relative_to(P('c:a/b')), P())
+        p = P('C:Foo/Bar')
+        self.assertEqual(p.relative_to(P('c:')), P('Foo/Bar'))
+        self.assertEqual(p.relative_to('c:'), P('Foo/Bar'))
+        self.assertEqual(p.relative_to(P('c:foO')), P('Bar'))
+        self.assertEqual(p.relative_to('c:foO'), P('Bar'))
+        self.assertEqual(p.relative_to('c:foO/'), P('Bar'))
+        self.assertEqual(p.relative_to(P('c:foO/baR')), P())
+        self.assertEqual(p.relative_to('c:foO/baR'), P())
         # Unrelated paths
         self.assertRaises(ValueError, p.relative_to, P())
+        self.assertRaises(ValueError, p.relative_to, '')
         self.assertRaises(ValueError, p.relative_to, P('d:'))
-        self.assertRaises(ValueError, p.relative_to, P('a'))
-        self.assertRaises(ValueError, p.relative_to, P('/a'))
-        self.assertRaises(ValueError, p.relative_to, P('c:a/b/c'))
-        self.assertRaises(ValueError, p.relative_to, P('c:a/c'))
-        self.assertRaises(ValueError, p.relative_to, P('c:/a'))
-        p = P('c:/a/b')
-        self.assertEqual(p.relative_to(P('c:')), P('/a/b'))
-        self.assertEqual(p.relative_to(P('c:/')), P('a/b'))
-        self.assertEqual(p.relative_to(P('c:/a')), P('b'))
-        self.assertEqual(p.relative_to(P('c:/a/b')), P())
+        self.assertRaises(ValueError, p.relative_to, P('/'))
+        self.assertRaises(ValueError, p.relative_to, P('Foo'))
+        self.assertRaises(ValueError, p.relative_to, P('/Foo'))
+        self.assertRaises(ValueError, p.relative_to, P('C:/Foo'))
+        self.assertRaises(ValueError, p.relative_to, P('C:Foo/Bar/Baz'))
+        self.assertRaises(ValueError, p.relative_to, P('C:Foo/Baz'))
+        p = P('C:/Foo/Bar')
+        self.assertEqual(p.relative_to(P('c:')), P('/Foo/Bar'))
+        self.assertEqual(p.relative_to('c:'), P('/Foo/Bar'))
+        self.assertEqual(str(p.relative_to(P('c:'))), '\\Foo\\Bar')
+        self.assertEqual(str(p.relative_to('c:')), '\\Foo\\Bar')
+        self.assertEqual(p.relative_to(P('c:/')), P('Foo/Bar'))
+        self.assertEqual(p.relative_to('c:/'), P('Foo/Bar'))
+        self.assertEqual(p.relative_to(P('c:/foO')), P('Bar'))
+        self.assertEqual(p.relative_to('c:/foO'), P('Bar'))
+        self.assertEqual(p.relative_to('c:/foO/'), P('Bar'))
+        self.assertEqual(p.relative_to(P('c:/foO/baR')), P())
+        self.assertEqual(p.relative_to('c:/foO/baR'), P())
         # Unrelated paths
-        self.assertRaises(ValueError, p.relative_to, P('c:/c'))
-        self.assertRaises(ValueError, p.relative_to, P('c:/a/b/c'))
-        self.assertRaises(ValueError, p.relative_to, P('c:/a/c'))
-        self.assertRaises(ValueError, p.relative_to, P('c:a'))
+        self.assertRaises(ValueError, p.relative_to, P('C:/Baz'))
+        self.assertRaises(ValueError, p.relative_to, P('C:/Foo/Bar/Baz'))
+        self.assertRaises(ValueError, p.relative_to, P('C:/Foo/Baz'))
+        self.assertRaises(ValueError, p.relative_to, P('C:Foo'))
         self.assertRaises(ValueError, p.relative_to, P('d:'))
         self.assertRaises(ValueError, p.relative_to, P('d:/'))
-        self.assertRaises(ValueError, p.relative_to, P('/a'))
-        self.assertRaises(ValueError, p.relative_to, P('//c/a'))
+        self.assertRaises(ValueError, p.relative_to, P('/'))
+        self.assertRaises(ValueError, p.relative_to, P('/Foo'))
+        self.assertRaises(ValueError, p.relative_to, P('//C/Foo'))
         # UNC paths
-        p = P('//a/b/c/d')
-        self.assertEqual(p.relative_to(P('//a/b')), P('c/d'))
-        self.assertEqual(p.relative_to(P('//a/b/c')), P('d'))
-        self.assertEqual(p.relative_to(P('//a/b/c/d')), P())
+        p = P('//Server/Share/Foo/Bar')
+        self.assertEqual(p.relative_to(P('//sErver/sHare')), P('Foo/Bar'))
+        self.assertEqual(p.relative_to('//sErver/sHare'), P('Foo/Bar'))
+        self.assertEqual(p.relative_to('//sErver/sHare/'), P('Foo/Bar'))
+        self.assertEqual(p.relative_to(P('//sErver/sHare/Foo')), P('Bar'))
+        self.assertEqual(p.relative_to('//sErver/sHare/Foo'), P('Bar'))
+        self.assertEqual(p.relative_to('//sErver/sHare/Foo/'), P('Bar'))
+        self.assertEqual(p.relative_to(P('//sErver/sHare/Foo/Bar')), P())
+        self.assertEqual(p.relative_to('//sErver/sHare/Foo/Bar'), P())
         # Unrelated paths
-        self.assertRaises(ValueError, p.relative_to, P('/a/b/c'))
-        self.assertRaises(ValueError, p.relative_to, P('c:/a/b/c'))
-        self.assertRaises(ValueError, p.relative_to, P('//z/b/c'))
-        self.assertRaises(ValueError, p.relative_to, P('//a/z/c'))
+        self.assertRaises(ValueError, p.relative_to, P('/Server/Share/Foo'))
+        self.assertRaises(ValueError, p.relative_to, P('c:/Server/Share/Foo'))
+        self.assertRaises(ValueError, p.relative_to, P('//z/Share/Foo'))
+        self.assertRaises(ValueError, p.relative_to, P('//Server/z/Foo'))
 
     def test_is_absolute(self):
         P = self.cls