mirror of https://github.com/python/cpython.git
GH-127807: pathlib ABCs: remove a few private attributes (#127851)
From `PurePathBase` delete `_globber`, `_stack` and `_pattern_str`, and from `PathBase` delete `_glob_selector`. This helps avoid an unpleasant surprise for a users who try to use these names.
This commit is contained in:
parent
a959ea1b0a
commit
f5ba74b819
|
@ -25,6 +25,23 @@ def _is_case_sensitive(parser):
|
||||||
return parser.normcase('Aa') == 'Aa'
|
return parser.normcase('Aa') == 'Aa'
|
||||||
|
|
||||||
|
|
||||||
|
def _explode_path(path):
|
||||||
|
"""
|
||||||
|
Split the path into a 2-tuple (anchor, parts), where *anchor* is the
|
||||||
|
uppermost parent of the path (equivalent to path.parents[-1]), and
|
||||||
|
*parts* is a reversed list of parts following the anchor.
|
||||||
|
"""
|
||||||
|
split = path.parser.split
|
||||||
|
path = str(path)
|
||||||
|
parent, name = split(path)
|
||||||
|
names = []
|
||||||
|
while path != parent:
|
||||||
|
names.append(name)
|
||||||
|
path = parent
|
||||||
|
parent, name = split(path)
|
||||||
|
return path, names
|
||||||
|
|
||||||
|
|
||||||
class PathGlobber(_GlobberBase):
|
class PathGlobber(_GlobberBase):
|
||||||
"""
|
"""
|
||||||
Class providing shell-style globbing for path objects.
|
Class providing shell-style globbing for path objects.
|
||||||
|
@ -50,7 +67,6 @@ class PurePathBase:
|
||||||
|
|
||||||
__slots__ = ()
|
__slots__ = ()
|
||||||
parser = posixpath
|
parser = posixpath
|
||||||
_globber = PathGlobber
|
|
||||||
|
|
||||||
def with_segments(self, *pathsegments):
|
def with_segments(self, *pathsegments):
|
||||||
"""Construct a new path object from any number of path-like objects.
|
"""Construct a new path object from any number of path-like objects.
|
||||||
|
@ -82,7 +98,7 @@ def root(self):
|
||||||
@property
|
@property
|
||||||
def anchor(self):
|
def anchor(self):
|
||||||
"""The concatenation of the drive and root, or ''."""
|
"""The concatenation of the drive and root, or ''."""
|
||||||
return self._stack[0]
|
return _explode_path(self)[0]
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def name(self):
|
def name(self):
|
||||||
|
@ -160,8 +176,8 @@ def relative_to(self, other, *, walk_up=False):
|
||||||
"""
|
"""
|
||||||
if not isinstance(other, PurePathBase):
|
if not isinstance(other, PurePathBase):
|
||||||
other = self.with_segments(other)
|
other = self.with_segments(other)
|
||||||
anchor0, parts0 = self._stack
|
anchor0, parts0 = _explode_path(self)
|
||||||
anchor1, parts1 = other._stack
|
anchor1, parts1 = _explode_path(other)
|
||||||
if anchor0 != anchor1:
|
if anchor0 != anchor1:
|
||||||
raise ValueError(f"{str(self)!r} and {str(other)!r} have different anchors")
|
raise ValueError(f"{str(self)!r} and {str(other)!r} have different anchors")
|
||||||
while parts0 and parts1 and parts0[-1] == parts1[-1]:
|
while parts0 and parts1 and parts0[-1] == parts1[-1]:
|
||||||
|
@ -183,8 +199,8 @@ def is_relative_to(self, other):
|
||||||
"""
|
"""
|
||||||
if not isinstance(other, PurePathBase):
|
if not isinstance(other, PurePathBase):
|
||||||
other = self.with_segments(other)
|
other = self.with_segments(other)
|
||||||
anchor0, parts0 = self._stack
|
anchor0, parts0 = _explode_path(self)
|
||||||
anchor1, parts1 = other._stack
|
anchor1, parts1 = _explode_path(other)
|
||||||
if anchor0 != anchor1:
|
if anchor0 != anchor1:
|
||||||
return False
|
return False
|
||||||
while parts0 and parts1 and parts0[-1] == parts1[-1]:
|
while parts0 and parts1 and parts0[-1] == parts1[-1]:
|
||||||
|
@ -199,7 +215,7 @@ def is_relative_to(self, other):
|
||||||
def parts(self):
|
def parts(self):
|
||||||
"""An object providing sequence-like access to the
|
"""An object providing sequence-like access to the
|
||||||
components in the filesystem path."""
|
components in the filesystem path."""
|
||||||
anchor, parts = self._stack
|
anchor, parts = _explode_path(self)
|
||||||
if anchor:
|
if anchor:
|
||||||
parts.append(anchor)
|
parts.append(anchor)
|
||||||
return tuple(reversed(parts))
|
return tuple(reversed(parts))
|
||||||
|
@ -224,23 +240,6 @@ def __rtruediv__(self, key):
|
||||||
except TypeError:
|
except TypeError:
|
||||||
return NotImplemented
|
return NotImplemented
|
||||||
|
|
||||||
@property
|
|
||||||
def _stack(self):
|
|
||||||
"""
|
|
||||||
Split the path into a 2-tuple (anchor, parts), where *anchor* is the
|
|
||||||
uppermost parent of the path (equivalent to path.parents[-1]), and
|
|
||||||
*parts* is a reversed list of parts following the anchor.
|
|
||||||
"""
|
|
||||||
split = self.parser.split
|
|
||||||
path = str(self)
|
|
||||||
parent, name = split(path)
|
|
||||||
names = []
|
|
||||||
while path != parent:
|
|
||||||
names.append(name)
|
|
||||||
path = parent
|
|
||||||
parent, name = split(path)
|
|
||||||
return path, names
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def parent(self):
|
def parent(self):
|
||||||
"""The logical parent of the path."""
|
"""The logical parent of the path."""
|
||||||
|
@ -268,11 +267,6 @@ def is_absolute(self):
|
||||||
a drive)."""
|
a drive)."""
|
||||||
return self.parser.isabs(str(self))
|
return self.parser.isabs(str(self))
|
||||||
|
|
||||||
@property
|
|
||||||
def _pattern_str(self):
|
|
||||||
"""The path expressed as a string, for use in pattern-matching."""
|
|
||||||
return str(self)
|
|
||||||
|
|
||||||
def match(self, path_pattern, *, case_sensitive=None):
|
def match(self, path_pattern, *, case_sensitive=None):
|
||||||
"""
|
"""
|
||||||
Return True if this path matches the given pattern. If the pattern is
|
Return True if this path matches the given pattern. If the pattern is
|
||||||
|
@ -293,7 +287,7 @@ def match(self, path_pattern, *, case_sensitive=None):
|
||||||
return False
|
return False
|
||||||
if len(path_parts) > len(pattern_parts) and path_pattern.anchor:
|
if len(path_parts) > len(pattern_parts) and path_pattern.anchor:
|
||||||
return False
|
return False
|
||||||
globber = self._globber(sep, case_sensitive)
|
globber = PathGlobber(sep, case_sensitive)
|
||||||
for path_part, pattern_part in zip(path_parts, pattern_parts):
|
for path_part, pattern_part in zip(path_parts, pattern_parts):
|
||||||
match = globber.compile(pattern_part)
|
match = globber.compile(pattern_part)
|
||||||
if match(path_part) is None:
|
if match(path_part) is None:
|
||||||
|
@ -309,9 +303,9 @@ def full_match(self, pattern, *, case_sensitive=None):
|
||||||
pattern = self.with_segments(pattern)
|
pattern = self.with_segments(pattern)
|
||||||
if case_sensitive is None:
|
if case_sensitive is None:
|
||||||
case_sensitive = _is_case_sensitive(self.parser)
|
case_sensitive = _is_case_sensitive(self.parser)
|
||||||
globber = self._globber(pattern.parser.sep, case_sensitive, recursive=True)
|
globber = PathGlobber(pattern.parser.sep, case_sensitive, recursive=True)
|
||||||
match = globber.compile(pattern._pattern_str)
|
match = globber.compile(str(pattern))
|
||||||
return match(self._pattern_str) is not None
|
return match(str(self)) is not None
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -463,29 +457,25 @@ def iterdir(self):
|
||||||
"""
|
"""
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
|
|
||||||
def _glob_selector(self, parts, case_sensitive, recurse_symlinks):
|
|
||||||
if case_sensitive is None:
|
|
||||||
case_sensitive = _is_case_sensitive(self.parser)
|
|
||||||
case_pedantic = False
|
|
||||||
else:
|
|
||||||
# The user has expressed a case sensitivity choice, but we don't
|
|
||||||
# know the case sensitivity of the underlying filesystem, so we
|
|
||||||
# must use scandir() for everything, including non-wildcard parts.
|
|
||||||
case_pedantic = True
|
|
||||||
recursive = True if recurse_symlinks else _no_recurse_symlinks
|
|
||||||
globber = self._globber(self.parser.sep, case_sensitive, case_pedantic, recursive)
|
|
||||||
return globber.selector(parts)
|
|
||||||
|
|
||||||
def glob(self, pattern, *, case_sensitive=None, recurse_symlinks=True):
|
def glob(self, pattern, *, case_sensitive=None, recurse_symlinks=True):
|
||||||
"""Iterate over this subtree and yield all existing files (of any
|
"""Iterate over this subtree and yield all existing files (of any
|
||||||
kind, including directories) matching the given relative pattern.
|
kind, including directories) matching the given relative pattern.
|
||||||
"""
|
"""
|
||||||
if not isinstance(pattern, PurePathBase):
|
if not isinstance(pattern, PurePathBase):
|
||||||
pattern = self.with_segments(pattern)
|
pattern = self.with_segments(pattern)
|
||||||
anchor, parts = pattern._stack
|
anchor, parts = _explode_path(pattern)
|
||||||
if anchor:
|
if anchor:
|
||||||
raise NotImplementedError("Non-relative patterns are unsupported")
|
raise NotImplementedError("Non-relative patterns are unsupported")
|
||||||
select = self._glob_selector(parts, case_sensitive, recurse_symlinks)
|
if case_sensitive is None:
|
||||||
|
case_sensitive = _is_case_sensitive(self.parser)
|
||||||
|
case_pedantic = False
|
||||||
|
elif case_sensitive == _is_case_sensitive(self.parser):
|
||||||
|
case_pedantic = False
|
||||||
|
else:
|
||||||
|
case_pedantic = True
|
||||||
|
recursive = True if recurse_symlinks else _no_recurse_symlinks
|
||||||
|
globber = PathGlobber(self.parser.sep, case_sensitive, case_pedantic, recursive)
|
||||||
|
select = globber.selector(parts)
|
||||||
return select(self)
|
return select(self)
|
||||||
|
|
||||||
def rglob(self, pattern, *, case_sensitive=None, recurse_symlinks=True):
|
def rglob(self, pattern, *, case_sensitive=None, recurse_symlinks=True):
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
import posixpath
|
import posixpath
|
||||||
import sys
|
import sys
|
||||||
from errno import EINVAL, EXDEV
|
from errno import EINVAL, EXDEV
|
||||||
from glob import _StringGlobber
|
from glob import _StringGlobber, _no_recurse_symlinks
|
||||||
from itertools import chain
|
from itertools import chain
|
||||||
from stat import S_ISSOCK, S_ISBLK, S_ISCHR, S_ISFIFO
|
from stat import S_ISSOCK, S_ISBLK, S_ISCHR, S_ISFIFO
|
||||||
from _collections_abc import Sequence
|
from _collections_abc import Sequence
|
||||||
|
@ -112,7 +112,6 @@ class PurePath(PurePathBase):
|
||||||
'_hash',
|
'_hash',
|
||||||
)
|
)
|
||||||
parser = os.path
|
parser = os.path
|
||||||
_globber = _StringGlobber
|
|
||||||
|
|
||||||
def __new__(cls, *args, **kwargs):
|
def __new__(cls, *args, **kwargs):
|
||||||
"""Construct a PurePath from one or several strings and or existing
|
"""Construct a PurePath from one or several strings and or existing
|
||||||
|
@ -513,13 +512,22 @@ def as_uri(self):
|
||||||
from urllib.parse import quote_from_bytes
|
from urllib.parse import quote_from_bytes
|
||||||
return prefix + quote_from_bytes(os.fsencode(path))
|
return prefix + quote_from_bytes(os.fsencode(path))
|
||||||
|
|
||||||
@property
|
def full_match(self, pattern, *, case_sensitive=None):
|
||||||
def _pattern_str(self):
|
"""
|
||||||
"""The path expressed as a string, for use in pattern-matching."""
|
Return True if this path matches the given glob-style pattern. The
|
||||||
|
pattern is matched against the entire path.
|
||||||
|
"""
|
||||||
|
if not isinstance(pattern, PurePathBase):
|
||||||
|
pattern = self.with_segments(pattern)
|
||||||
|
if case_sensitive is None:
|
||||||
|
case_sensitive = self.parser is posixpath
|
||||||
|
|
||||||
# The string representation of an empty path is a single dot ('.'). Empty
|
# The string representation of an empty path is a single dot ('.'). Empty
|
||||||
# paths shouldn't match wildcards, so we change it to the empty string.
|
# paths shouldn't match wildcards, so we change it to the empty string.
|
||||||
path_str = str(self)
|
path = str(self) if self.parts else ''
|
||||||
return '' if path_str == '.' else path_str
|
pattern = str(pattern) if pattern.parts else ''
|
||||||
|
globber = _StringGlobber(self.parser.sep, case_sensitive, recursive=True)
|
||||||
|
return globber.compile(pattern)(path) is not None
|
||||||
|
|
||||||
# Subclassing os.PathLike makes isinstance() checks slower,
|
# Subclassing os.PathLike makes isinstance() checks slower,
|
||||||
# which in turn makes Path construction slower. Register instead!
|
# which in turn makes Path construction slower. Register instead!
|
||||||
|
@ -749,8 +757,18 @@ def glob(self, pattern, *, case_sensitive=None, recurse_symlinks=False):
|
||||||
kind, including directories) matching the given relative pattern.
|
kind, including directories) matching the given relative pattern.
|
||||||
"""
|
"""
|
||||||
sys.audit("pathlib.Path.glob", self, pattern)
|
sys.audit("pathlib.Path.glob", self, pattern)
|
||||||
|
if case_sensitive is None:
|
||||||
|
case_sensitive = self.parser is posixpath
|
||||||
|
case_pedantic = False
|
||||||
|
else:
|
||||||
|
# The user has expressed a case sensitivity choice, but we don't
|
||||||
|
# know the case sensitivity of the underlying filesystem, so we
|
||||||
|
# must use scandir() for everything, including non-wildcard parts.
|
||||||
|
case_pedantic = True
|
||||||
parts = self._parse_pattern(pattern)
|
parts = self._parse_pattern(pattern)
|
||||||
select = self._glob_selector(parts[::-1], case_sensitive, recurse_symlinks)
|
recursive = True if recurse_symlinks else _no_recurse_symlinks
|
||||||
|
globber = _StringGlobber(self.parser.sep, case_sensitive, case_pedantic, recursive)
|
||||||
|
select = globber.selector(parts[::-1])
|
||||||
root = str(self)
|
root = str(self)
|
||||||
paths = select(root)
|
paths = select(root)
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue