Skip to content

Commit 28e7831

Browse files
committed
gh-100809: Fix handling of drive-relative paths in pathlib.Path.absolute()
If it's available, use `nt._getfullpathname()` to retrieve an absolute path. This allows paths such as 'X:' to be made absolute even when `os.getcwd()` returns a path on another drive. It follows the behaviour of `os.path.abspath()`, except that no path normalisation is performed.
1 parent 7fba99e commit 28e7831

3 files changed

Lines changed: 29 additions & 5 deletions

File tree

Lib/pathlib.py

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,17 @@
1919
from operator import attrgetter
2020
from stat import S_ISDIR, S_ISLNK, S_ISREG, S_ISSOCK, S_ISBLK, S_ISCHR, S_ISFIFO
2121
from urllib.parse import quote_from_bytes as urlquote_from_bytes
22+
try:
23+
from nt import _getfullpathname
24+
except ImportError:
25+
def _absolute_parts(path):
26+
return [os.getcwd()] + path._parts
27+
else:
28+
def _absolute_parts(path):
29+
try:
30+
return [_getfullpathname(path)]
31+
except (OSError, ValueError):
32+
return [os.getcwd()] + path._parts
2233

2334

2435
__all__ = [
@@ -827,7 +838,7 @@ def absolute(self):
827838
"""
828839
if self.is_absolute():
829840
return self
830-
return self._from_parts([os.getcwd()] + self._parts)
841+
return self._from_parts(_absolute_parts(self))
831842

832843
def resolve(self, strict=False):
833844
"""

Lib/test/test_pathlib.py

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1554,8 +1554,7 @@ def test_cwd(self):
15541554
def test_absolute_common(self):
15551555
P = self.cls
15561556

1557-
with mock.patch("os.getcwd") as getcwd:
1558-
getcwd.return_value = BASE
1557+
with os_helper.change_cwd(BASE):
15591558

15601559
# Simple relative paths.
15611560
self.assertEqual(str(P().absolute()), BASE)
@@ -2993,15 +2992,26 @@ def test_absolute(self):
29932992
self.assertEqual(str(P(share + 'a\\b').absolute()), share + 'a\\b')
29942993

29952994
# UNC relative paths.
2996-
with mock.patch("os.getcwd") as getcwd:
2997-
getcwd.return_value = share
2995+
def getfullpathname(path):
2996+
return os.path.join(share, path)
29982997

2998+
with mock.patch("nt._getfullpathname", getfullpathname):
29992999
self.assertEqual(str(P().absolute()), share)
30003000
self.assertEqual(str(P('.').absolute()), share)
30013001
self.assertEqual(str(P('a').absolute()), os.path.join(share, 'a'))
30023002
self.assertEqual(str(P('a', 'b', 'c').absolute()),
30033003
os.path.join(share, 'a', 'b', 'c'))
30043004

3005+
drive = os.path.splitdrive(BASE)[0]
3006+
with os_helper.change_cwd(BASE):
3007+
# Relative path with drive
3008+
self.assertEqual(str(P(drive).absolute()), BASE)
3009+
self.assertEqual(str(P(drive + 'foo').absolute()), os.path.join(BASE, 'foo'))
3010+
3011+
# Relative path with root
3012+
self.assertEqual(str(P('\\').absolute()), drive + '\\')
3013+
self.assertEqual(str(P('\\foo').absolute()), drive + '\\foo')
3014+
30053015

30063016
def test_glob(self):
30073017
P = self.cls
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
Fix handling of drive-relative paths (like 'C:' and 'C:foo') in
2+
:meth:`pathlib.Path.absolute`. This method now calls
3+
``nt._getfullpathname()`` on Windows.

0 commit comments

Comments
 (0)