diff --git a/Lib/pathlib/_abc.py b/Lib/pathlib/_abc.py index e9e46e511bd..2c243d470d4 100644 --- a/Lib/pathlib/_abc.py +++ b/Lib/pathlib/_abc.py @@ -735,7 +735,13 @@ class PathBase(PurePathBase): 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 def cwd(cls): @@ -772,10 +778,13 @@ class PathBase(PurePathBase): """ if self._resolving: return self + elif self.parser is not posixpath: + raise UnsupportedOperation(self._unsupported_msg('resolve()')) - def getcwd(): - return str(self.with_segments().absolute()) + def raise_error(*args): + raise OSError("Unsupported operation.") + getcwd = raise_error if strict or getattr(self.readlink, '_supported', True): def lstat(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 # symlinks are unsupported and (in non-strict mode) we can improve # performance by not calling `path.lstat()`. - def skip(path_str): - # This exception will be internally consumed by `_realpath()`. - raise OSError("Operation skipped.") - - lstat = readlink = skip + lstat = readlink = raise_error 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, maxlinks=self._max_symlinks)) diff --git a/Lib/test/test_pathlib/test_pathlib.py b/Lib/test/test_pathlib/test_pathlib.py index c7104bfda90..46966b6df2d 100644 --- a/Lib/test/test_pathlib/test_pathlib.py +++ b/Lib/test/test_pathlib/test_pathlib.py @@ -861,6 +861,28 @@ class PathTest(test_pathlib_abc.DummyPathTest, PurePathTest): def test_move_into_empty_name_other_os(self): 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): p = self.cls('non', 'exist') diff --git a/Lib/test/test_pathlib/test_pathlib_abc.py b/Lib/test/test_pathlib/test_pathlib_abc.py index bb2e4187ef9..b69d674e1cf 100644 --- a/Lib/test/test_pathlib/test_pathlib_abc.py +++ b/Lib/test/test_pathlib/test_pathlib_abc.py @@ -2493,6 +2493,23 @@ class DummyPathTest(DummyPurePathTest): bad_link.symlink_to("bad" * 200) 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 def test_readlink(self): P = self.cls(self.base) @@ -2810,29 +2827,6 @@ class DummyPathTest(DummyPurePathTest): self.assertEqual(p, P) 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 def test_complex_symlinks_absolute(self): self._check_complex_symlinks(self.base)