Skip to content

Commit

Permalink
Sync from upstream.
Browse files Browse the repository at this point in the history
  • Loading branch information
barneygale committed Jan 8, 2024
1 parent c5f0a0c commit 9922853
Show file tree
Hide file tree
Showing 3 changed files with 106 additions and 212 deletions.
2 changes: 1 addition & 1 deletion cpython
Submodule cpython updated 121 files
130 changes: 39 additions & 91 deletions pathlib_abc/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,6 @@
ntpath = object()
from . import _posixpath as posixpath
import sys
import warnings
from _collections_abc import Sequence
from errno import ENOENT, ENOTDIR, EBADF, ELOOP, EINVAL
from itertools import chain
from stat import S_ISDIR, S_ISLNK, S_ISREG, S_ISSOCK, S_ISBLK, S_ISCHR, S_ISFIFO
Expand All @@ -14,9 +12,6 @@
# Internals
#

# Maximum number of symlinks to follow in PathBase.resolve()
_MAX_SYMLINKS = 40

# Reference for Windows paths can be found at
# https://learn.microsoft.com/en-gb/windows/win32/fileio/naming-a-file .
_WIN_RESERVED_NAMES = frozenset(
Expand Down Expand Up @@ -142,35 +137,6 @@ class UnsupportedOperation(NotImplementedError):
pass


class _PathParents(Sequence):
"""This object provides sequence-like access to the logical ancestors
of a path. Don't try to construct it yourself."""
__slots__ = ('_path', '_drv', '_root', '_tail')

def __init__(self, path):
self._path = path
self._drv = path.drive
self._root = path.root
self._tail = path._tail

def __len__(self):
return len(self._tail)

def __getitem__(self, idx):
if isinstance(idx, slice):
return tuple(self[i] for i in range(*idx.indices(len(self))))

if idx >= len(self) or idx < -len(self):
raise IndexError(idx)
if idx < 0:
idx += len(self)
return self._path._from_parsed_parts(self._drv, self._root,
self._tail[:-idx - 1])

def __repr__(self):
return "<{}.parents>".format(type(self._path).__name__)


class PurePathBase(abc.ABC):
"""Base class for pure path objects.
Expand Down Expand Up @@ -317,10 +283,10 @@ def anchor(self):
@property
def name(self):
"""The final path component, if any."""
tail = self._tail
if not tail:
path_str = str(self)
if not path_str or path_str == '.':
return ''
return tail[-1]
return self.pathmod.basename(path_str)

@property
def suffix(self):
Expand Down Expand Up @@ -364,11 +330,10 @@ def with_name(self, name):
m = self.pathmod
if not name or m.sep in name or (m.altsep and m.altsep in name) or name == '.':
raise ValueError(f"Invalid name {name!r}")
tail = self._tail.copy()
if not tail:
parent, old_name = m.split(str(self))
if not old_name or old_name == '.':
raise ValueError(f"{self!r} has an empty name")
tail[-1] = name
return self._from_parsed_parts(self.drive, self.root, tail)
return self.with_segments(parent, name)

def with_stem(self, stem):
"""Return a new path with the stem changed."""
Expand All @@ -386,21 +351,15 @@ def with_suffix(self, suffix):
else:
raise ValueError(f"Invalid suffix {suffix!r}")

def relative_to(self, other, /, *_deprecated, walk_up=False):
def relative_to(self, other, *, walk_up=False):
"""Return the relative path to another path identified by the passed
arguments. If the operation is not possible (because this is not
related to the other path), raise ValueError.
The *walk_up* parameter controls whether `..` may be used to resolve
the path.
"""
if _deprecated:
msg = ("support for supplying more than one positional argument "
"to pathlib.PurePath.relative_to() is deprecated and "
"scheduled for removal in Python 3.14")
warnings.warn(msg, DeprecationWarning, stacklevel=2)
other = self.with_segments(other, *_deprecated)
elif not isinstance(other, PurePathBase):
if not isinstance(other, PurePathBase):
other = self.with_segments(other)
for step, path in enumerate(chain([other], other.parents)):
if path == self or path in self.parents:
Expand All @@ -412,18 +371,12 @@ def relative_to(self, other, /, *_deprecated, walk_up=False):
else:
raise ValueError(f"{str(self)!r} and {str(other)!r} have different anchors")
parts = ['..'] * step + self._tail[len(path._tail):]
return self._from_parsed_parts('', '', parts)
return self.with_segments(*parts)

def is_relative_to(self, other, /, *_deprecated):
def is_relative_to(self, other):
"""Return True if the path is relative to another path or False.
"""
if _deprecated:
msg = ("support for supplying more than one argument to "
"pathlib.PurePath.is_relative_to() is deprecated and "
"scheduled for removal in Python 3.14")
warnings.warn(msg, DeprecationWarning, stacklevel=2)
other = self.with_segments(other, *_deprecated)
elif not isinstance(other, PurePathBase):
if not isinstance(other, PurePathBase):
other = self.with_segments(other)
return other == self or other in self.parents

Expand Down Expand Up @@ -459,21 +412,26 @@ def __rtruediv__(self, key):
@property
def parent(self):
"""The logical parent of the path."""
drv = self.drive
root = self.root
tail = self._tail
if not tail:
return self
path = self._from_parsed_parts(drv, root, tail[:-1])
path._resolving = self._resolving
return path
path = str(self)
parent = self.pathmod.dirname(path)
if path != parent:
parent = self.with_segments(parent)
parent._resolving = self._resolving
return parent
return self

@property
def parents(self):
"""A sequence of this path's logical parents."""
# The value of this property should not be cached on the path object,
# as doing so would introduce a reference cycle.
return _PathParents(self)
dirname = self.pathmod.dirname
path = str(self)
parent = dirname(path)
parents = []
while path != parent:
parents.append(self.with_segments(parent))
path = parent
parent = dirname(path)
return tuple(parents)

def is_absolute(self):
"""True if the path is absolute (has both a root and, if applicable,
Expand Down Expand Up @@ -542,6 +500,9 @@ class PathBase(PurePathBase):
"""
__slots__ = ()

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

@classmethod
def _unsupported(cls, method_name):
msg = f"{cls.__name__}.{method_name}() is unsupported"
Expand Down Expand Up @@ -814,18 +775,6 @@ def glob(self, pattern, *, case_sensitive=None, follow_symlinks=None):
"""Iterate over this subtree and yield all existing files (of any
kind, including directories) matching the given relative pattern.
"""
sys.audit("pathlib.Path.glob", self, pattern)
return self._glob(pattern, case_sensitive, follow_symlinks)

def rglob(self, pattern, *, case_sensitive=None, follow_symlinks=None):
"""Recursively yield all existing files (of any kind, including
directories) matching the given relative pattern, anywhere in
this subtree.
"""
sys.audit("pathlib.Path.rglob", self, pattern)
return self._glob(f'**/{pattern}', case_sensitive, follow_symlinks)

def _glob(self, pattern, case_sensitive, follow_symlinks):
path_pattern = self.with_segments(pattern)
if path_pattern.drive or path_pattern.root:
raise NotImplementedError("Non-relative patterns are unsupported")
Expand All @@ -836,14 +785,6 @@ def _glob(self, pattern, case_sensitive, follow_symlinks):
if pattern[-1] in (self.pathmod.sep, self.pathmod.altsep):
# GH-65238: pathlib doesn't preserve trailing slash. Add it back.
pattern_parts.append('')
if pattern_parts[-1] == '**':
# GH-70303: '**' only matches directories. Add trailing slash.
warnings.warn(
"Pattern ending '**' will match files and directories in a "
"future Python release. Add a trailing slash to match only "
"directories and remove this warning.",
FutureWarning, 3)
pattern_parts.append('')

if case_sensitive is None:
# TODO: evaluate case-sensitivity of each directory in _select_children().
Expand Down Expand Up @@ -898,9 +839,16 @@ def _glob(self, pattern, case_sensitive, follow_symlinks):
paths = _select_children(paths, dir_only, follow_symlinks, match)
return paths

def rglob(self, pattern, *, case_sensitive=None, follow_symlinks=None):
"""Recursively yield all existing files (of any kind, including
directories) matching the given relative pattern, anywhere in
this subtree.
"""
return self.glob(
f'**/{pattern}', case_sensitive=case_sensitive, follow_symlinks=follow_symlinks)

def walk(self, top_down=True, on_error=None, follow_symlinks=False):
"""Walk the directory tree from this directory, similar to os.walk()."""
sys.audit("pathlib.Path.walk", self, on_error, follow_symlinks)
paths = [self]

while paths:
Expand Down Expand Up @@ -1026,7 +974,7 @@ def resolve(self, strict=False):
# Like Linux and macOS, raise OSError(errno.ELOOP) if too many symlinks are
# encountered during resolution.
link_count += 1
if link_count >= _MAX_SYMLINKS:
if link_count >= self._max_symlinks:
raise OSError(ELOOP, "Too many symbolic links in path", str(self))
target, target_parts = next_path.readlink()._split_stack()
# If the symlink target is absolute (like '/etc/hosts'), set the current
Expand Down
Loading

0 comments on commit 9922853

Please sign in to comment.