mirror of
https://github.com/python/cpython.git
synced 2024-11-21 21:09:37 +01:00
pathlib ABCs: tighten up resolve()
and absolute()
(#126611)
In `PathBase.resolve()`, raise `UnsupportedOperation` if a non-POSIX path parser is used (our implementation uses `posixpath._realpath()`, which produces incorrect results for non-POSIX path flavours.) Also tweak code to call `self.absolute()` upfront rather than supplying an emulated `getcwd()` function. Adjust `PathBase.absolute()` to work somewhat like `resolve()`. If a POSIX path parser is used, we treat the root directory as the current directory. This is the simplest useful behaviour for concrete path types without a current directory cursor.
This commit is contained in:
parent
0f47a3199c
commit
266328552e
@ -735,7 +735,13 @@ class PathBase(PurePathBase):
|
|||||||
|
|
||||||
Use resolve() to resolve symlinks and remove '..' segments.
|
Use resolve() to resolve symlinks and remove '..' segments.
|
||||||
"""
|
"""
|
||||||
raise UnsupportedOperation(self._unsupported_msg('absolute()'))
|
if self.is_absolute():
|
||||||
|
return self
|
||||||
|
elif self.parser is not posixpath:
|
||||||
|
raise UnsupportedOperation(self._unsupported_msg('absolute()'))
|
||||||
|
else:
|
||||||
|
# Treat the root directory as the current working directory.
|
||||||
|
return self.with_segments('/', *self._raw_paths)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def cwd(cls):
|
def cwd(cls):
|
||||||
@ -772,10 +778,13 @@ class PathBase(PurePathBase):
|
|||||||
"""
|
"""
|
||||||
if self._resolving:
|
if self._resolving:
|
||||||
return self
|
return self
|
||||||
|
elif self.parser is not posixpath:
|
||||||
|
raise UnsupportedOperation(self._unsupported_msg('resolve()'))
|
||||||
|
|
||||||
def getcwd():
|
def raise_error(*args):
|
||||||
return str(self.with_segments().absolute())
|
raise OSError("Unsupported operation.")
|
||||||
|
|
||||||
|
getcwd = raise_error
|
||||||
if strict or getattr(self.readlink, '_supported', True):
|
if strict or getattr(self.readlink, '_supported', True):
|
||||||
def lstat(path_str):
|
def lstat(path_str):
|
||||||
path = self.with_segments(path_str)
|
path = self.with_segments(path_str)
|
||||||
@ -790,14 +799,10 @@ class PathBase(PurePathBase):
|
|||||||
# If the user has *not* overridden the `readlink()` method, then
|
# If the user has *not* overridden the `readlink()` method, then
|
||||||
# symlinks are unsupported and (in non-strict mode) we can improve
|
# symlinks are unsupported and (in non-strict mode) we can improve
|
||||||
# performance by not calling `path.lstat()`.
|
# performance by not calling `path.lstat()`.
|
||||||
def skip(path_str):
|
lstat = readlink = raise_error
|
||||||
# This exception will be internally consumed by `_realpath()`.
|
|
||||||
raise OSError("Operation skipped.")
|
|
||||||
|
|
||||||
lstat = readlink = skip
|
|
||||||
|
|
||||||
return self.with_segments(posixpath._realpath(
|
return self.with_segments(posixpath._realpath(
|
||||||
str(self), strict, self.parser.sep,
|
str(self.absolute()), strict, self.parser.sep,
|
||||||
getcwd=getcwd, lstat=lstat, readlink=readlink,
|
getcwd=getcwd, lstat=lstat, readlink=readlink,
|
||||||
maxlinks=self._max_symlinks))
|
maxlinks=self._max_symlinks))
|
||||||
|
|
||||||
|
@ -861,6 +861,28 @@ class PathTest(test_pathlib_abc.DummyPathTest, PurePathTest):
|
|||||||
def test_move_into_empty_name_other_os(self):
|
def test_move_into_empty_name_other_os(self):
|
||||||
self.test_move_into_empty_name()
|
self.test_move_into_empty_name()
|
||||||
|
|
||||||
|
def _check_complex_symlinks(self, link0_target):
|
||||||
|
super()._check_complex_symlinks(link0_target)
|
||||||
|
P = self.cls(self.base)
|
||||||
|
# Resolve relative paths.
|
||||||
|
old_path = os.getcwd()
|
||||||
|
os.chdir(self.base)
|
||||||
|
try:
|
||||||
|
p = self.cls('link0').resolve()
|
||||||
|
self.assertEqual(p, P)
|
||||||
|
self.assertEqualNormCase(str(p), self.base)
|
||||||
|
p = self.cls('link1').resolve()
|
||||||
|
self.assertEqual(p, P)
|
||||||
|
self.assertEqualNormCase(str(p), self.base)
|
||||||
|
p = self.cls('link2').resolve()
|
||||||
|
self.assertEqual(p, P)
|
||||||
|
self.assertEqualNormCase(str(p), self.base)
|
||||||
|
p = self.cls('link3').resolve()
|
||||||
|
self.assertEqual(p, P)
|
||||||
|
self.assertEqualNormCase(str(p), self.base)
|
||||||
|
finally:
|
||||||
|
os.chdir(old_path)
|
||||||
|
|
||||||
def test_resolve_nonexist_relative_issue38671(self):
|
def test_resolve_nonexist_relative_issue38671(self):
|
||||||
p = self.cls('non', 'exist')
|
p = self.cls('non', 'exist')
|
||||||
|
|
||||||
|
@ -2493,6 +2493,23 @@ class DummyPathTest(DummyPurePathTest):
|
|||||||
bad_link.symlink_to("bad" * 200)
|
bad_link.symlink_to("bad" * 200)
|
||||||
self.assertEqual(sorted(base.glob('**/*')), [bad_link])
|
self.assertEqual(sorted(base.glob('**/*')), [bad_link])
|
||||||
|
|
||||||
|
@needs_posix
|
||||||
|
def test_absolute_posix(self):
|
||||||
|
P = self.cls
|
||||||
|
# The default implementation uses '/' as the current directory
|
||||||
|
self.assertEqual(str(P('').absolute()), '/')
|
||||||
|
self.assertEqual(str(P('a').absolute()), '/a')
|
||||||
|
self.assertEqual(str(P('a/b').absolute()), '/a/b')
|
||||||
|
|
||||||
|
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')
|
||||||
|
|
||||||
@needs_symlinks
|
@needs_symlinks
|
||||||
def test_readlink(self):
|
def test_readlink(self):
|
||||||
P = self.cls(self.base)
|
P = self.cls(self.base)
|
||||||
@ -2810,29 +2827,6 @@ class DummyPathTest(DummyPurePathTest):
|
|||||||
self.assertEqual(p, P)
|
self.assertEqual(p, P)
|
||||||
self.assertEqualNormCase(str(p), self.base)
|
self.assertEqualNormCase(str(p), self.base)
|
||||||
|
|
||||||
# Resolve relative paths.
|
|
||||||
try:
|
|
||||||
self.cls('').absolute()
|
|
||||||
except UnsupportedOperation:
|
|
||||||
return
|
|
||||||
old_path = os.getcwd()
|
|
||||||
os.chdir(self.base)
|
|
||||||
try:
|
|
||||||
p = self.cls('link0').resolve()
|
|
||||||
self.assertEqual(p, P)
|
|
||||||
self.assertEqualNormCase(str(p), self.base)
|
|
||||||
p = self.cls('link1').resolve()
|
|
||||||
self.assertEqual(p, P)
|
|
||||||
self.assertEqualNormCase(str(p), self.base)
|
|
||||||
p = self.cls('link2').resolve()
|
|
||||||
self.assertEqual(p, P)
|
|
||||||
self.assertEqualNormCase(str(p), self.base)
|
|
||||||
p = self.cls('link3').resolve()
|
|
||||||
self.assertEqual(p, P)
|
|
||||||
self.assertEqualNormCase(str(p), self.base)
|
|
||||||
finally:
|
|
||||||
os.chdir(old_path)
|
|
||||||
|
|
||||||
@needs_symlinks
|
@needs_symlinks
|
||||||
def test_complex_symlinks_absolute(self):
|
def test_complex_symlinks_absolute(self):
|
||||||
self._check_complex_symlinks(self.base)
|
self._check_complex_symlinks(self.base)
|
||||||
|
Loading…
Reference in New Issue
Block a user