"""
if not hasattr(other, 'with_segments'):
other = self.with_segments(other)
- for step, path in enumerate(chain([other], other.parents)):
+ parts = []
+ for path in chain([other], other.parents):
if path == self or path in self.parents:
break
elif not walk_up:
raise ValueError(f"{str(self)!r} is not in the subpath of {str(other)!r}")
elif path.name == '..':
raise ValueError(f"'..' segment in {str(other)!r} cannot be walked")
+ else:
+ parts.append('..')
else:
raise ValueError(f"{str(self)!r} and {str(other)!r} have different anchors")
- parts = ['..'] * step + self._tail[len(path._tail):]
+ parts.extend(self._tail[len(path._tail):])
return self._from_parsed_parts('', '', parts)
def is_relative_to(self, other):
parent = split(path)[0]
return tuple(parents)
+ def relative_to(self, other, *, walk_up=False):
+ """Return the relative path to another path identified by the passed
+ arguments. If the operation is not possible (because this is not
+ related to the other path), raise ValueError.
+
+ The *walk_up* parameter controls whether `..` may be used to resolve
+ the path.
+ """
+ parts = []
+ for path in (other,) + other.parents:
+ if self.is_relative_to(path):
+ break
+ elif not walk_up:
+ raise ValueError(f"{self!r} is not in the subpath of {other!r}")
+ elif path.name == '..':
+ raise ValueError(f"'..' segment in {other!r} cannot be walked")
+ else:
+ parts.append('..')
+ else:
+ raise ValueError(f"{self!r} and {other!r} have different anchors")
+ return self.with_segments(*parts, *self.parts[len(path.parts):])
+
+ def is_relative_to(self, other):
+ """Return True if the path is relative to another path or False.
+ """
+ return other == self or other in self.parents
+
def full_match(self, pattern):
"""
Return True if this path matches the given glob-style pattern. The
self.assertRaises(ValueError, P('a/b').with_suffix, '.d/.')
self.assertRaises(TypeError, P('a/b').with_suffix, None)
+ def test_relative_to(self):
+ P = self.cls
+ p = P('a/b')
+ self.assertEqual(p.relative_to(P('')), P('a', 'b'))
+ self.assertEqual(p.relative_to(P('a')), P('b'))
+ self.assertEqual(p.relative_to(P('a/b')), P(''))
+ self.assertEqual(p.relative_to(P(''), walk_up=True), P('a', 'b'))
+ self.assertEqual(p.relative_to(P('a'), walk_up=True), P('b'))
+ self.assertEqual(p.relative_to(P('a/b'), walk_up=True), P(''))
+ self.assertEqual(p.relative_to(P('a/c'), walk_up=True), P('..', 'b'))
+ self.assertEqual(p.relative_to(P('a/b/c'), walk_up=True), P('..'))
+ self.assertEqual(p.relative_to(P('c'), walk_up=True), P('..', 'a', 'b'))
+ 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('/a'))
+ self.assertRaises(ValueError, p.relative_to, P('../a'))
+ self.assertRaises(ValueError, p.relative_to, P('a/..'))
+ self.assertRaises(ValueError, p.relative_to, P('/a/..'))
+ self.assertRaises(ValueError, p.relative_to, P('/'), walk_up=True)
+ self.assertRaises(ValueError, p.relative_to, P('/a'), walk_up=True)
+ self.assertRaises(ValueError, p.relative_to, P('../a'), walk_up=True)
+ self.assertRaises(ValueError, p.relative_to, P('a/..'), walk_up=True)
+ self.assertRaises(ValueError, p.relative_to, P('/a/..'), walk_up=True)
+ class Q(self.cls):
+ __eq__ = object.__eq__
+ __hash__ = object.__hash__
+ q = Q('a/b')
+ self.assertTrue(q.relative_to(q))
+ self.assertRaises(ValueError, q.relative_to, Q(''))
+ self.assertRaises(ValueError, q.relative_to, Q('a'))
+ self.assertRaises(ValueError, q.relative_to, Q('a'), walk_up=True)
+ self.assertRaises(ValueError, q.relative_to, Q('a/b'))
+ self.assertRaises(ValueError, q.relative_to, Q('c'))
+
+ def test_is_relative_to(self):
+ P = self.cls
+ p = P('a/b')
+ self.assertTrue(p.is_relative_to(P('')))
+ self.assertTrue(p.is_relative_to(P('a')))
+ self.assertTrue(p.is_relative_to(P('a/b')))
+ self.assertFalse(p.is_relative_to(P('c')))
+ self.assertFalse(p.is_relative_to(P('a/b/c')))
+ self.assertFalse(p.is_relative_to(P('a/c')))
+ self.assertFalse(p.is_relative_to(P('/a')))
+ class Q(self.cls):
+ __eq__ = object.__eq__
+ __hash__ = object.__hash__
+ q = Q('a/b')
+ self.assertTrue(q.is_relative_to(q))
+ self.assertFalse(q.is_relative_to(Q('')))
+ self.assertFalse(q.is_relative_to(Q('a')))
+ self.assertFalse(q.is_relative_to(Q('a/b')))
+ self.assertFalse(q.is_relative_to(Q('c')))
+
class LexicalPathJoinTest(JoinTestBase, unittest.TestCase):
cls = LexicalPath