Skip to content

Commit 9db71d9

Browse files
shihai1991icanhasmath
authored andcommitted
bpo-37689: add Path.is_relative_to() method (pythonGH-14982)
1 parent 735dbcc commit 9db71d9

File tree

4 files changed

+111
-1
lines changed

4 files changed

+111
-1
lines changed

Doc/library/pathlib.rst

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -273,7 +273,7 @@ Methods and properties
273273

274274
.. testsetup::
275275

276-
from pathlib import PurePosixPath, PureWindowsPath
276+
from pathlib import PurePath, PurePosixPath, PureWindowsPath
277277

278278
Pure paths provide the following methods and properties:
279279

@@ -462,6 +462,19 @@ Pure paths provide the following methods and properties:
462462
True
463463

464464

465+
.. method:: PurePath.is_relative_to(*other)
466+
467+
Return whether or not this path is relative to the *other* path.
468+
469+
>>> p = PurePath('/etc/passwd')
470+
>>> p.is_relative_to('/etc')
471+
True
472+
>>> p.is_relative_to('/usr')
473+
False
474+
475+
.. versionadded:: 3.9
476+
477+
465478
.. method:: PurePath.is_reserved()
466479

467480
With :class:`PureWindowsPath`, return ``True`` if the path is considered

Lib/pathlib.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -901,6 +901,15 @@ def relative_to(self, *other):
901901
return self._from_parsed_parts('', root if n == 1 else '',
902902
abs_parts[n:])
903903

904+
def is_relative_to(self, *other):
905+
"""Return True if the path is relative to another path or False.
906+
"""
907+
try:
908+
self.relative_to(*other)
909+
return True
910+
except ValueError:
911+
return False
912+
904913
@property
905914
def parts(self):
906915
"""An object providing sequence-like access to the

Lib/test/test_pathlib.py

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -616,6 +616,40 @@ def test_relative_to_common(self):
616616
self.assertRaises(ValueError, p.relative_to, '')
617617
self.assertRaises(ValueError, p.relative_to, P('a'))
618618

619+
def test_is_relative_to_common(self):
620+
P = self.cls
621+
p = P('a/b')
622+
self.assertRaises(TypeError, p.is_relative_to)
623+
self.assertRaises(TypeError, p.is_relative_to, b'a')
624+
self.assertTrue(p.is_relative_to(P()))
625+
self.assertTrue(p.is_relative_to(''))
626+
self.assertTrue(p.is_relative_to(P('a')))
627+
self.assertTrue(p.is_relative_to('a/'))
628+
self.assertTrue(p.is_relative_to(P('a/b')))
629+
self.assertTrue(p.is_relative_to('a/b'))
630+
# With several args.
631+
self.assertTrue(p.is_relative_to('a', 'b'))
632+
# Unrelated paths.
633+
self.assertFalse(p.is_relative_to(P('c')))
634+
self.assertFalse(p.is_relative_to(P('a/b/c')))
635+
self.assertFalse(p.is_relative_to(P('a/c')))
636+
self.assertFalse(p.is_relative_to(P('/a')))
637+
p = P('/a/b')
638+
self.assertTrue(p.is_relative_to(P('/')))
639+
self.assertTrue(p.is_relative_to('/'))
640+
self.assertTrue(p.is_relative_to(P('/a')))
641+
self.assertTrue(p.is_relative_to('/a'))
642+
self.assertTrue(p.is_relative_to('/a/'))
643+
self.assertTrue(p.is_relative_to(P('/a/b')))
644+
self.assertTrue(p.is_relative_to('/a/b'))
645+
# Unrelated paths.
646+
self.assertFalse(p.is_relative_to(P('/c')))
647+
self.assertFalse(p.is_relative_to(P('/a/b/c')))
648+
self.assertFalse(p.is_relative_to(P('/a/c')))
649+
self.assertFalse(p.is_relative_to(P()))
650+
self.assertFalse(p.is_relative_to(''))
651+
self.assertFalse(p.is_relative_to(P('a')))
652+
619653
def test_pickling_common(self):
620654
P = self.cls
621655
p = P('/a/b')
@@ -1059,6 +1093,59 @@ def test_relative_to(self):
10591093
self.assertRaises(ValueError, p.relative_to, P('//z/Share/Foo'))
10601094
self.assertRaises(ValueError, p.relative_to, P('//Server/z/Foo'))
10611095

1096+
def test_is_relative_to(self):
1097+
P = self.cls
1098+
p = P('C:Foo/Bar')
1099+
self.assertTrue(p.is_relative_to(P('c:')))
1100+
self.assertTrue(p.is_relative_to('c:'))
1101+
self.assertTrue(p.is_relative_to(P('c:foO')))
1102+
self.assertTrue(p.is_relative_to('c:foO'))
1103+
self.assertTrue(p.is_relative_to('c:foO/'))
1104+
self.assertTrue(p.is_relative_to(P('c:foO/baR')))
1105+
self.assertTrue(p.is_relative_to('c:foO/baR'))
1106+
# Unrelated paths.
1107+
self.assertFalse(p.is_relative_to(P()))
1108+
self.assertFalse(p.is_relative_to(''))
1109+
self.assertFalse(p.is_relative_to(P('d:')))
1110+
self.assertFalse(p.is_relative_to(P('/')))
1111+
self.assertFalse(p.is_relative_to(P('Foo')))
1112+
self.assertFalse(p.is_relative_to(P('/Foo')))
1113+
self.assertFalse(p.is_relative_to(P('C:/Foo')))
1114+
self.assertFalse(p.is_relative_to(P('C:Foo/Bar/Baz')))
1115+
self.assertFalse(p.is_relative_to(P('C:Foo/Baz')))
1116+
p = P('C:/Foo/Bar')
1117+
self.assertTrue(p.is_relative_to('c:'))
1118+
self.assertTrue(p.is_relative_to(P('c:/')))
1119+
self.assertTrue(p.is_relative_to(P('c:/foO')))
1120+
self.assertTrue(p.is_relative_to('c:/foO/'))
1121+
self.assertTrue(p.is_relative_to(P('c:/foO/baR')))
1122+
self.assertTrue(p.is_relative_to('c:/foO/baR'))
1123+
# Unrelated paths.
1124+
self.assertFalse(p.is_relative_to(P('C:/Baz')))
1125+
self.assertFalse(p.is_relative_to(P('C:/Foo/Bar/Baz')))
1126+
self.assertFalse(p.is_relative_to(P('C:/Foo/Baz')))
1127+
self.assertFalse(p.is_relative_to(P('C:Foo')))
1128+
self.assertFalse(p.is_relative_to(P('d:')))
1129+
self.assertFalse(p.is_relative_to(P('d:/')))
1130+
self.assertFalse(p.is_relative_to(P('/')))
1131+
self.assertFalse(p.is_relative_to(P('/Foo')))
1132+
self.assertFalse(p.is_relative_to(P('//C/Foo')))
1133+
# UNC paths.
1134+
p = P('//Server/Share/Foo/Bar')
1135+
self.assertTrue(p.is_relative_to(P('//sErver/sHare')))
1136+
self.assertTrue(p.is_relative_to('//sErver/sHare'))
1137+
self.assertTrue(p.is_relative_to('//sErver/sHare/'))
1138+
self.assertTrue(p.is_relative_to(P('//sErver/sHare/Foo')))
1139+
self.assertTrue(p.is_relative_to('//sErver/sHare/Foo'))
1140+
self.assertTrue(p.is_relative_to('//sErver/sHare/Foo/'))
1141+
self.assertTrue(p.is_relative_to(P('//sErver/sHare/Foo/Bar')))
1142+
self.assertTrue(p.is_relative_to('//sErver/sHare/Foo/Bar'))
1143+
# Unrelated paths.
1144+
self.assertFalse(p.is_relative_to(P('/Server/Share/Foo')))
1145+
self.assertFalse(p.is_relative_to(P('c:/Server/Share/Foo')))
1146+
self.assertFalse(p.is_relative_to(P('//z/Share/Foo')))
1147+
self.assertFalse(p.is_relative_to(P('//Server/z/Foo')))
1148+
10621149
def test_is_absolute(self):
10631150
P = self.cls
10641151
# Under NT, only paths with both a drive and a root are absolute
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Add :meth:`is_relative_to` in :class:`PurePath` to determine whether or not one path is relative to another.

0 commit comments

Comments
 (0)