bpo-29688: document and test `pathlib.Path.absolute()` (GH-26153)

Co-authored-by: Brett Cannon <brett@python.org>
Co-authored-by: Brian Helba <brian.helba@kitware.com>
This commit is contained in:
Barney Gale 2022-01-28 23:40:55 +00:00 committed by GitHub
parent 1f036ede59
commit 18cb2ef46c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 78 additions and 11 deletions

View File

@ -1052,6 +1052,18 @@ call fails (for example because the path doesn't exist).
Added return value, return the new Path instance.
.. method:: Path.absolute()
Make the path absolute, without normalization or resolving symlinks.
Returns a new path object::
>>> p = Path('tests')
>>> p
PosixPath('tests')
>>> p.absolute()
PosixPath('/home/antoine/pathlib/tests')
.. method:: Path.resolve(strict=False)
Make the path absolute, resolving any symlinks. A new path object is
@ -1239,13 +1251,14 @@ Below is a table mapping various :mod:`os` functions to their corresponding
Not all pairs of functions/methods below are equivalent. Some of them,
despite having some overlapping use-cases, have different semantics. They
include :func:`os.path.abspath` and :meth:`Path.resolve`,
include :func:`os.path.abspath` and :meth:`Path.absolute`,
:func:`os.path.relpath` and :meth:`PurePath.relative_to`.
==================================== ==============================
:mod:`os` and :mod:`os.path` :mod:`pathlib`
==================================== ==============================
:func:`os.path.abspath` :meth:`Path.resolve` [#]_
:func:`os.path.abspath` :meth:`Path.absolute` [#]_
:func:`os.path.realpath` :meth:`Path.resolve`
:func:`os.chmod` :meth:`Path.chmod`
:func:`os.mkdir` :meth:`Path.mkdir`
:func:`os.makedirs` :meth:`Path.mkdir`
@ -1278,5 +1291,5 @@ Below is a table mapping various :mod:`os` functions to their corresponding
.. rubric:: Footnotes
.. [#] :func:`os.path.abspath` does not resolve symbolic links while :meth:`Path.resolve` does.
.. [#] :func:`os.path.abspath` normalizes the resulting path, which may change its meaning in the presence of symlinks, while :meth:`Path.absolute` does not.
.. [#] :meth:`Path.relative_to` requires ``self`` to be the subpath of the argument, but :func:`os.path.relpath` does not.

View File

@ -1043,24 +1043,19 @@ def rglob(self, pattern):
yield p
def absolute(self):
"""Return an absolute version of this path. This function works
even if the path doesn't point to anything.
"""Return an absolute version of this path by prepending the current
working directory. No normalization or symlink resolution is performed.
No normalization is done, i.e. all '.' and '..' will be kept along.
Use resolve() to get the canonical path to a file.
"""
# XXX untested yet!
if self.is_absolute():
return self
# FIXME this must defer to the specific flavour (and, under Windows,
# use nt._getfullpathname())
return self._from_parts([self._accessor.getcwd()] + self._parts)
def resolve(self, strict=False):
"""
Make the path absolute, resolving all symlinks on the way and also
normalizing it (for example turning slashes into backslashes under
Windows).
normalizing it.
"""
def check_eloop(e):

View File

@ -1456,6 +1456,28 @@ def test_cwd(self):
p = self.cls.cwd()
self._test_cwd(p)
def test_absolute_common(self):
P = self.cls
with mock.patch("pathlib._normal_accessor.getcwd") as getcwd:
getcwd.return_value = BASE
# Simple relative paths.
self.assertEqual(str(P().absolute()), BASE)
self.assertEqual(str(P('.').absolute()), BASE)
self.assertEqual(str(P('a').absolute()), os.path.join(BASE, 'a'))
self.assertEqual(str(P('a', 'b', 'c').absolute()), os.path.join(BASE, 'a', 'b', 'c'))
# Symlinks should not be resolved.
self.assertEqual(str(P('linkB', 'fileB').absolute()), os.path.join(BASE, 'linkB', 'fileB'))
self.assertEqual(str(P('brokenLink').absolute()), os.path.join(BASE, 'brokenLink'))
self.assertEqual(str(P('brokenLinkLoop').absolute()), os.path.join(BASE, 'brokenLinkLoop'))
# '..' entries should be preserved and not normalised.
self.assertEqual(str(P('..').absolute()), os.path.join(BASE, '..'))
self.assertEqual(str(P('a', '..').absolute()), os.path.join(BASE, 'a', '..'))
self.assertEqual(str(P('..', 'b').absolute()), os.path.join(BASE, '..', 'b'))
def _test_home(self, p):
q = self.cls(os.path.expanduser('~'))
self.assertEqual(p, q)
@ -2463,6 +2485,17 @@ def test_glob_empty_pattern(self):
class PosixPathTest(_BasePathTest, unittest.TestCase):
cls = pathlib.PosixPath
def test_absolute(self):
P = self.cls
self.assertEqual(str(P('/').absolute()), '/')
self.assertEqual(str(P('/a').absolute()), '/a')
self.assertEqual(str(P('/a/b').absolute()), '/a/b')
# '//'-prefixed absolute path (supported by POSIX).
self.assertEqual(str(P('//').absolute()), '//')
self.assertEqual(str(P('//a').absolute()), '//a')
self.assertEqual(str(P('//a/b').absolute()), '//a/b')
def _check_symlink_loop(self, *args, strict=True):
path = self.cls(*args)
with self.assertRaises(RuntimeError):
@ -2628,6 +2661,31 @@ def test_handling_bad_descriptor(self):
class WindowsPathTest(_BasePathTest, unittest.TestCase):
cls = pathlib.WindowsPath
def test_absolute(self):
P = self.cls
# Simple absolute paths.
self.assertEqual(str(P('c:\\').absolute()), 'c:\\')
self.assertEqual(str(P('c:\\a').absolute()), 'c:\\a')
self.assertEqual(str(P('c:\\a\\b').absolute()), 'c:\\a\\b')
# UNC absolute paths.
share = '\\\\server\\share\\'
self.assertEqual(str(P(share).absolute()), share)
self.assertEqual(str(P(share + 'a').absolute()), share + 'a')
self.assertEqual(str(P(share + 'a\\b').absolute()), share + 'a\\b')
# UNC relative paths.
with mock.patch("pathlib._normal_accessor.getcwd") as getcwd:
getcwd.return_value = share
self.assertEqual(str(P().absolute()), share)
self.assertEqual(str(P('.').absolute()), share)
self.assertEqual(str(P('a').absolute()), os.path.join(share, 'a'))
self.assertEqual(str(P('a', 'b', 'c').absolute()),
os.path.join(share, 'a', 'b', 'c'))
def test_glob(self):
P = self.cls
p = P(BASE)

View File

@ -0,0 +1 @@
Document :meth:`pathlib.Path.absolute` (which has always existed).