Skip to content

Commit 18cb2ef

Browse files
barneygalebrettcannonbrianhelba
authored
bpo-29688: document and test pathlib.Path.absolute() (GH-26153)
Co-authored-by: Brett Cannon <[email protected]> Co-authored-by: Brian Helba <[email protected]>
1 parent 1f036ed commit 18cb2ef

File tree

4 files changed

+78
-11
lines changed

4 files changed

+78
-11
lines changed

Doc/library/pathlib.rst

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1052,6 +1052,18 @@ call fails (for example because the path doesn't exist).
10521052
Added return value, return the new Path instance.
10531053

10541054

1055+
.. method:: Path.absolute()
1056+
1057+
Make the path absolute, without normalization or resolving symlinks.
1058+
Returns a new path object::
1059+
1060+
>>> p = Path('tests')
1061+
>>> p
1062+
PosixPath('tests')
1063+
>>> p.absolute()
1064+
PosixPath('/home/antoine/pathlib/tests')
1065+
1066+
10551067
.. method:: Path.resolve(strict=False)
10561068

10571069
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
12391251

12401252
Not all pairs of functions/methods below are equivalent. Some of them,
12411253
despite having some overlapping use-cases, have different semantics. They
1242-
include :func:`os.path.abspath` and :meth:`Path.resolve`,
1254+
include :func:`os.path.abspath` and :meth:`Path.absolute`,
12431255
:func:`os.path.relpath` and :meth:`PurePath.relative_to`.
12441256

12451257
==================================== ==============================
12461258
:mod:`os` and :mod:`os.path` :mod:`pathlib`
12471259
==================================== ==============================
1248-
:func:`os.path.abspath` :meth:`Path.resolve` [#]_
1260+
:func:`os.path.abspath` :meth:`Path.absolute` [#]_
1261+
:func:`os.path.realpath` :meth:`Path.resolve`
12491262
:func:`os.chmod` :meth:`Path.chmod`
12501263
:func:`os.mkdir` :meth:`Path.mkdir`
12511264
:func:`os.makedirs` :meth:`Path.mkdir`
@@ -1278,5 +1291,5 @@ Below is a table mapping various :mod:`os` functions to their corresponding
12781291

12791292
.. rubric:: Footnotes
12801293

1281-
.. [#] :func:`os.path.abspath` does not resolve symbolic links while :meth:`Path.resolve` does.
1294+
.. [#] :func:`os.path.abspath` normalizes the resulting path, which may change its meaning in the presence of symlinks, while :meth:`Path.absolute` does not.
12821295
.. [#] :meth:`Path.relative_to` requires ``self`` to be the subpath of the argument, but :func:`os.path.relpath` does not.

Lib/pathlib.py

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1043,24 +1043,19 @@ def rglob(self, pattern):
10431043
yield p
10441044

10451045
def absolute(self):
1046-
"""Return an absolute version of this path. This function works
1047-
even if the path doesn't point to anything.
1046+
"""Return an absolute version of this path by prepending the current
1047+
working directory. No normalization or symlink resolution is performed.
10481048
1049-
No normalization is done, i.e. all '.' and '..' will be kept along.
10501049
Use resolve() to get the canonical path to a file.
10511050
"""
1052-
# XXX untested yet!
10531051
if self.is_absolute():
10541052
return self
1055-
# FIXME this must defer to the specific flavour (and, under Windows,
1056-
# use nt._getfullpathname())
10571053
return self._from_parts([self._accessor.getcwd()] + self._parts)
10581054

10591055
def resolve(self, strict=False):
10601056
"""
10611057
Make the path absolute, resolving all symlinks on the way and also
1062-
normalizing it (for example turning slashes into backslashes under
1063-
Windows).
1058+
normalizing it.
10641059
"""
10651060

10661061
def check_eloop(e):

Lib/test/test_pathlib.py

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1456,6 +1456,28 @@ def test_cwd(self):
14561456
p = self.cls.cwd()
14571457
self._test_cwd(p)
14581458

1459+
def test_absolute_common(self):
1460+
P = self.cls
1461+
1462+
with mock.patch("pathlib._normal_accessor.getcwd") as getcwd:
1463+
getcwd.return_value = BASE
1464+
1465+
# Simple relative paths.
1466+
self.assertEqual(str(P().absolute()), BASE)
1467+
self.assertEqual(str(P('.').absolute()), BASE)
1468+
self.assertEqual(str(P('a').absolute()), os.path.join(BASE, 'a'))
1469+
self.assertEqual(str(P('a', 'b', 'c').absolute()), os.path.join(BASE, 'a', 'b', 'c'))
1470+
1471+
# Symlinks should not be resolved.
1472+
self.assertEqual(str(P('linkB', 'fileB').absolute()), os.path.join(BASE, 'linkB', 'fileB'))
1473+
self.assertEqual(str(P('brokenLink').absolute()), os.path.join(BASE, 'brokenLink'))
1474+
self.assertEqual(str(P('brokenLinkLoop').absolute()), os.path.join(BASE, 'brokenLinkLoop'))
1475+
1476+
# '..' entries should be preserved and not normalised.
1477+
self.assertEqual(str(P('..').absolute()), os.path.join(BASE, '..'))
1478+
self.assertEqual(str(P('a', '..').absolute()), os.path.join(BASE, 'a', '..'))
1479+
self.assertEqual(str(P('..', 'b').absolute()), os.path.join(BASE, '..', 'b'))
1480+
14591481
def _test_home(self, p):
14601482
q = self.cls(os.path.expanduser('~'))
14611483
self.assertEqual(p, q)
@@ -2463,6 +2485,17 @@ def test_glob_empty_pattern(self):
24632485
class PosixPathTest(_BasePathTest, unittest.TestCase):
24642486
cls = pathlib.PosixPath
24652487

2488+
def test_absolute(self):
2489+
P = self.cls
2490+
self.assertEqual(str(P('/').absolute()), '/')
2491+
self.assertEqual(str(P('/a').absolute()), '/a')
2492+
self.assertEqual(str(P('/a/b').absolute()), '/a/b')
2493+
2494+
# '//'-prefixed absolute path (supported by POSIX).
2495+
self.assertEqual(str(P('//').absolute()), '//')
2496+
self.assertEqual(str(P('//a').absolute()), '//a')
2497+
self.assertEqual(str(P('//a/b').absolute()), '//a/b')
2498+
24662499
def _check_symlink_loop(self, *args, strict=True):
24672500
path = self.cls(*args)
24682501
with self.assertRaises(RuntimeError):
@@ -2628,6 +2661,31 @@ def test_handling_bad_descriptor(self):
26282661
class WindowsPathTest(_BasePathTest, unittest.TestCase):
26292662
cls = pathlib.WindowsPath
26302663

2664+
def test_absolute(self):
2665+
P = self.cls
2666+
2667+
# Simple absolute paths.
2668+
self.assertEqual(str(P('c:\\').absolute()), 'c:\\')
2669+
self.assertEqual(str(P('c:\\a').absolute()), 'c:\\a')
2670+
self.assertEqual(str(P('c:\\a\\b').absolute()), 'c:\\a\\b')
2671+
2672+
# UNC absolute paths.
2673+
share = '\\\\server\\share\\'
2674+
self.assertEqual(str(P(share).absolute()), share)
2675+
self.assertEqual(str(P(share + 'a').absolute()), share + 'a')
2676+
self.assertEqual(str(P(share + 'a\\b').absolute()), share + 'a\\b')
2677+
2678+
# UNC relative paths.
2679+
with mock.patch("pathlib._normal_accessor.getcwd") as getcwd:
2680+
getcwd.return_value = share
2681+
2682+
self.assertEqual(str(P().absolute()), share)
2683+
self.assertEqual(str(P('.').absolute()), share)
2684+
self.assertEqual(str(P('a').absolute()), os.path.join(share, 'a'))
2685+
self.assertEqual(str(P('a', 'b', 'c').absolute()),
2686+
os.path.join(share, 'a', 'b', 'c'))
2687+
2688+
26312689
def test_glob(self):
26322690
P = self.cls
26332691
p = P(BASE)
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Document :meth:`pathlib.Path.absolute` (which has always existed).

0 commit comments

Comments
 (0)