Skip to content

Commit

Permalink
GH-127381: pathlib ABCs: remove PathBase.resolve() and absolute() (
Browse files Browse the repository at this point in the history
…#127707)

Remove our implementation of POSIX path resolution in `PathBase.resolve()`.
This functionality is rather fragile and isn't necessary in most cases. It
depends on `PathBase.stat()`, which we're looking to remove.

Also remove `PathBase.absolute()`. Many legitimate virtual filesystems lack
the notion of a 'current directory', so it's wrong to include in the basic
interface.
  • Loading branch information
barneygale authored Dec 6, 2024
1 parent 0fc4063 commit 31c9f3c
Show file tree
Hide file tree
Showing 3 changed files with 599 additions and 731 deletions.
64 changes: 1 addition & 63 deletions Lib/pathlib/_abc.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@

import functools
import operator
import posixpath
from errno import EINVAL
from glob import _GlobberBase, _no_recurse_symlinks
from stat import S_ISDIR, S_ISLNK, S_ISREG, S_ISSOCK, S_ISBLK, S_ISCHR, S_ISFIFO
Expand Down Expand Up @@ -115,11 +114,6 @@ class PurePathBase:
# The `_raw_paths` slot stores unjoined string paths. This is set in
# the `__init__()` method.
'_raw_paths',

# The '_resolving' slot stores a boolean indicating whether the path
# is being processed by `PathBase.resolve()`. This prevents duplicate
# work from occurring when `resolve()` calls `stat()` or `readlink()`.
'_resolving',
)
parser = ParserBase()
_globber = PathGlobber
Expand All @@ -130,7 +124,6 @@ def __init__(self, *args):
raise TypeError(
f"argument should be a str, not {type(arg).__name__!r}")
self._raw_paths = list(args)
self._resolving = False

def with_segments(self, *pathsegments):
"""Construct a new path object from any number of path-like objects.
Expand Down Expand Up @@ -339,9 +332,7 @@ def parent(self):
path = str(self)
parent = self.parser.split(path)[0]
if path != parent:
parent = self.with_segments(parent)
parent._resolving = self._resolving
return parent
return self.with_segments(parent)
return self

@property
Expand Down Expand Up @@ -424,9 +415,6 @@ class PathBase(PurePathBase):
"""
__slots__ = ()

# Maximum number of symlinks to follow in resolve()
_max_symlinks = 40

@classmethod
def _unsupported_msg(cls, attribute):
return f"{cls.__name__}.{attribute} is unsupported"
Expand Down Expand Up @@ -720,20 +708,6 @@ def walk(self, top_down=True, on_error=None, follow_symlinks=False):
yield path, dirnames, filenames
paths += [path.joinpath(d) for d in reversed(dirnames)]

def absolute(self):
"""Return an absolute version of this path
No normalization or symlink resolution is performed.
Use resolve() to resolve symlinks and remove '..' segments.
"""
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)

def expanduser(self):
""" Return a new path with expanded ~ and ~user constructs
(as returned by os.path.expanduser)
Expand All @@ -745,42 +719,6 @@ def readlink(self):
Return the path to which the symbolic link points.
"""
raise UnsupportedOperation(self._unsupported_msg('readlink()'))
readlink._supported = False

def resolve(self, strict=False):
"""
Make the path absolute, resolving all symlinks on the way and also
normalizing it.
"""
if self._resolving:
return self
elif self.parser is not posixpath:
raise UnsupportedOperation(self._unsupported_msg('resolve()'))

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)
path._resolving = True
return path.stat(follow_symlinks=False)

def readlink(path_str):
path = self.with_segments(path_str)
path._resolving = True
return str(path.readlink())
else:
# 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()`.
lstat = readlink = raise_error

return self.with_segments(posixpath._realpath(
str(self.absolute()), strict, self.parser.sep,
getcwd=getcwd, lstat=lstat, readlink=readlink,
maxlinks=self._max_symlinks))

def symlink_to(self, target, target_is_directory=False):
"""
Expand Down
Loading

0 comments on commit 31c9f3c

Please sign in to comment.