Skip to content

Commit

Permalink
bpo-37689: add Path.is_relative_to() method (pythonGH-14982)
Browse files Browse the repository at this point in the history
  • Loading branch information
shihai1991 authored and icanhasmath committed Jul 19, 2024
1 parent 3018b22 commit b9f909d
Show file tree
Hide file tree
Showing 4 changed files with 111 additions and 1 deletion.
15 changes: 14 additions & 1 deletion Doc/library/pathlib.rst
Original file line number Diff line number Diff line change
Expand Up @@ -273,7 +273,7 @@ Methods and properties

.. testsetup::

from pathlib import PurePosixPath, PureWindowsPath
from pathlib import PurePath, PurePosixPath, PureWindowsPath

Pure paths provide the following methods and properties:

Expand Down Expand Up @@ -462,6 +462,19 @@ Pure paths provide the following methods and properties:
True


.. method:: PurePath.is_relative_to(*other)

Return whether or not this path is relative to the *other* path.

>>> p = PurePath('/etc/passwd')
>>> p.is_relative_to('/etc')
True
>>> p.is_relative_to('/usr')
False

.. versionadded:: 3.9


.. method:: PurePath.is_reserved()

With :class:`PureWindowsPath`, return ``True`` if the path is considered
Expand Down
9 changes: 9 additions & 0 deletions Lib/pathlib.py
Original file line number Diff line number Diff line change
Expand Up @@ -901,6 +901,15 @@ def relative_to(self, *other):
return self._from_parsed_parts('', root if n == 1 else '',
abs_parts[n:])

def is_relative_to(self, *other):
"""Return True if the path is relative to another path or False.
"""
try:
self.relative_to(*other)
return True
except ValueError:
return False

@property
def parts(self):
"""An object providing sequence-like access to the
Expand Down
87 changes: 87 additions & 0 deletions Lib/test/test_pathlib.py
Original file line number Diff line number Diff line change
Expand Up @@ -616,6 +616,40 @@ def test_relative_to_common(self):
self.assertRaises(ValueError, p.relative_to, '')
self.assertRaises(ValueError, p.relative_to, P('a'))

def test_is_relative_to_common(self):
P = self.cls
p = P('a/b')
self.assertRaises(TypeError, p.is_relative_to)
self.assertRaises(TypeError, p.is_relative_to, b'a')
self.assertTrue(p.is_relative_to(P()))
self.assertTrue(p.is_relative_to(''))
self.assertTrue(p.is_relative_to(P('a')))
self.assertTrue(p.is_relative_to('a/'))
self.assertTrue(p.is_relative_to(P('a/b')))
self.assertTrue(p.is_relative_to('a/b'))
# With several args.
self.assertTrue(p.is_relative_to('a', 'b'))
# Unrelated paths.
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')))
p = P('/a/b')
self.assertTrue(p.is_relative_to(P('/')))
self.assertTrue(p.is_relative_to('/'))
self.assertTrue(p.is_relative_to(P('/a')))
self.assertTrue(p.is_relative_to('/a'))
self.assertTrue(p.is_relative_to('/a/'))
self.assertTrue(p.is_relative_to(P('/a/b')))
self.assertTrue(p.is_relative_to('/a/b'))
# Unrelated paths.
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()))
self.assertFalse(p.is_relative_to(''))
self.assertFalse(p.is_relative_to(P('a')))

def test_pickling_common(self):
P = self.cls
p = P('/a/b')
Expand Down Expand Up @@ -1059,6 +1093,59 @@ def test_relative_to(self):
self.assertRaises(ValueError, p.relative_to, P('//z/Share/Foo'))
self.assertRaises(ValueError, p.relative_to, P('//Server/z/Foo'))

def test_is_relative_to(self):
P = self.cls
p = P('C:Foo/Bar')
self.assertTrue(p.is_relative_to(P('c:')))
self.assertTrue(p.is_relative_to('c:'))
self.assertTrue(p.is_relative_to(P('c:foO')))
self.assertTrue(p.is_relative_to('c:foO'))
self.assertTrue(p.is_relative_to('c:foO/'))
self.assertTrue(p.is_relative_to(P('c:foO/baR')))
self.assertTrue(p.is_relative_to('c:foO/baR'))
# Unrelated paths.
self.assertFalse(p.is_relative_to(P()))
self.assertFalse(p.is_relative_to(''))
self.assertFalse(p.is_relative_to(P('d:')))
self.assertFalse(p.is_relative_to(P('/')))
self.assertFalse(p.is_relative_to(P('Foo')))
self.assertFalse(p.is_relative_to(P('/Foo')))
self.assertFalse(p.is_relative_to(P('C:/Foo')))
self.assertFalse(p.is_relative_to(P('C:Foo/Bar/Baz')))
self.assertFalse(p.is_relative_to(P('C:Foo/Baz')))
p = P('C:/Foo/Bar')
self.assertTrue(p.is_relative_to('c:'))
self.assertTrue(p.is_relative_to(P('c:/')))
self.assertTrue(p.is_relative_to(P('c:/foO')))
self.assertTrue(p.is_relative_to('c:/foO/'))
self.assertTrue(p.is_relative_to(P('c:/foO/baR')))
self.assertTrue(p.is_relative_to('c:/foO/baR'))
# Unrelated paths.
self.assertFalse(p.is_relative_to(P('C:/Baz')))
self.assertFalse(p.is_relative_to(P('C:/Foo/Bar/Baz')))
self.assertFalse(p.is_relative_to(P('C:/Foo/Baz')))
self.assertFalse(p.is_relative_to(P('C:Foo')))
self.assertFalse(p.is_relative_to(P('d:')))
self.assertFalse(p.is_relative_to(P('d:/')))
self.assertFalse(p.is_relative_to(P('/')))
self.assertFalse(p.is_relative_to(P('/Foo')))
self.assertFalse(p.is_relative_to(P('//C/Foo')))
# UNC paths.
p = P('//Server/Share/Foo/Bar')
self.assertTrue(p.is_relative_to(P('//sErver/sHare')))
self.assertTrue(p.is_relative_to('//sErver/sHare'))
self.assertTrue(p.is_relative_to('//sErver/sHare/'))
self.assertTrue(p.is_relative_to(P('//sErver/sHare/Foo')))
self.assertTrue(p.is_relative_to('//sErver/sHare/Foo'))
self.assertTrue(p.is_relative_to('//sErver/sHare/Foo/'))
self.assertTrue(p.is_relative_to(P('//sErver/sHare/Foo/Bar')))
self.assertTrue(p.is_relative_to('//sErver/sHare/Foo/Bar'))
# Unrelated paths.
self.assertFalse(p.is_relative_to(P('/Server/Share/Foo')))
self.assertFalse(p.is_relative_to(P('c:/Server/Share/Foo')))
self.assertFalse(p.is_relative_to(P('//z/Share/Foo')))
self.assertFalse(p.is_relative_to(P('//Server/z/Foo')))

def test_is_absolute(self):
P = self.cls
# Under NT, only paths with both a drive and a root are absolute
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Add :meth:`is_relative_to` in :class:`PurePath` to determine whether or not one path is relative to another.

0 comments on commit b9f909d

Please sign in to comment.