Skip to content

Commit

Permalink
pythongh-100809: Fix handling of drive-relative paths in pathlib.Path…
Browse files Browse the repository at this point in the history
….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.
  • Loading branch information
barneygale committed Jan 6, 2023
1 parent 7fba99e commit 28e7831
Show file tree
Hide file tree
Showing 3 changed files with 29 additions and 5 deletions.
13 changes: 12 additions & 1 deletion Lib/pathlib.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,17 @@
from operator import attrgetter
from stat import S_ISDIR, S_ISLNK, S_ISREG, S_ISSOCK, S_ISBLK, S_ISCHR, S_ISFIFO
from urllib.parse import quote_from_bytes as urlquote_from_bytes
try:
from nt import _getfullpathname
except ImportError:
def _absolute_parts(path):
return [os.getcwd()] + path._parts
else:
def _absolute_parts(path):
try:
return [_getfullpathname(path)]
except (OSError, ValueError):
return [os.getcwd()] + path._parts


__all__ = [
Expand Down Expand Up @@ -827,7 +838,7 @@ def absolute(self):
"""
if self.is_absolute():
return self
return self._from_parts([os.getcwd()] + self._parts)
return self._from_parts(_absolute_parts(self))

def resolve(self, strict=False):
"""
Expand Down
18 changes: 14 additions & 4 deletions Lib/test/test_pathlib.py
Original file line number Diff line number Diff line change
Expand Up @@ -1554,8 +1554,7 @@ def test_cwd(self):
def test_absolute_common(self):
P = self.cls

with mock.patch("os.getcwd") as getcwd:
getcwd.return_value = BASE
with os_helper.change_cwd(BASE):

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

# UNC relative paths.
with mock.patch("os.getcwd") as getcwd:
getcwd.return_value = share
def getfullpathname(path):
return os.path.join(share, path)

with mock.patch("nt._getfullpathname", getfullpathname):
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'))

drive = os.path.splitdrive(BASE)[0]
with os_helper.change_cwd(BASE):
# Relative path with drive
self.assertEqual(str(P(drive).absolute()), BASE)
self.assertEqual(str(P(drive + 'foo').absolute()), os.path.join(BASE, 'foo'))

# Relative path with root
self.assertEqual(str(P('\\').absolute()), drive + '\\')
self.assertEqual(str(P('\\foo').absolute()), drive + '\\foo')


def test_glob(self):
P = self.cls
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
Fix handling of drive-relative paths (like 'C:' and 'C:foo') in
:meth:`pathlib.Path.absolute`. This method now calls
``nt._getfullpathname()`` on Windows.

0 comments on commit 28e7831

Please sign in to comment.