summaryrefslogtreecommitdiffstats
path: root/Lib
diff options
context:
space:
mode:
authorBarney Gale <barney.gale@gmail.com>2024-12-22 01:41:38 (GMT)
committerGitHub <noreply@github.com>2024-12-22 01:41:38 (GMT)
commitf5ba74b81979b621e38be70ec3ddad1e7f1365ce (patch)
tree283b43babf3d9e55ac2442ea4bb711be24cf13a9 /Lib
parenta959ea1b0a026ff118975b9b539513b06dde3190 (diff)
downloadcpython-f5ba74b81979b621e38be70ec3ddad1e7f1365ce.zip
cpython-f5ba74b81979b621e38be70ec3ddad1e7f1365ce.tar.gz
cpython-f5ba74b81979b621e38be70ec3ddad1e7f1365ce.tar.bz2
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.
Diffstat (limited to 'Lib')
-rw-r--r--Lib/pathlib/_abc.py86
-rw-r--r--Lib/pathlib/_local.py34
2 files changed, 64 insertions, 56 deletions
diff --git a/Lib/pathlib/_abc.py b/Lib/pathlib/_abc.py
index 4402efe..b521c75 100644
--- a/Lib/pathlib/_abc.py
+++ b/Lib/pathlib/_abc.py
@@ -25,6 +25,23 @@ def _is_case_sensitive(parser):
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 providing shell-style globbing for path objects.
@@ -50,7 +67,6 @@ class PurePathBase:
__slots__ = ()
parser = posixpath
- _globber = PathGlobber
def with_segments(self, *pathsegments):
"""Construct a new path object from any number of path-like objects.
@@ -82,7 +98,7 @@ class PurePathBase:
@property
def anchor(self):
"""The concatenation of the drive and root, or ''."""
- return self._stack[0]
+ return _explode_path(self)[0]
@property
def name(self):
@@ -160,8 +176,8 @@ class PurePathBase:
"""
if not isinstance(other, PurePathBase):
other = self.with_segments(other)
- anchor0, parts0 = self._stack
- anchor1, parts1 = other._stack
+ anchor0, parts0 = _explode_path(self)
+ anchor1, parts1 = _explode_path(other)
if anchor0 != anchor1:
raise ValueError(f"{str(self)!r} and {str(other)!r} have different anchors")
while parts0 and parts1 and parts0[-1] == parts1[-1]:
@@ -183,8 +199,8 @@ class PurePathBase:
"""
if not isinstance(other, PurePathBase):
other = self.with_segments(other)
- anchor0, parts0 = self._stack
- anchor1, parts1 = other._stack
+ anchor0, parts0 = _explode_path(self)
+ anchor1, parts1 = _explode_path(other)
if anchor0 != anchor1:
return False
while parts0 and parts1 and parts0[-1] == parts1[-1]:
@@ -199,7 +215,7 @@ class PurePathBase:
def parts(self):
"""An object providing sequence-like access to the
components in the filesystem path."""
- anchor, parts = self._stack
+ anchor, parts = _explode_path(self)
if anchor:
parts.append(anchor)
return tuple(reversed(parts))
@@ -225,23 +241,6 @@ class PurePathBase:
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
def parent(self):
"""The logical parent of the path."""
path = str(self)
@@ -268,11 +267,6 @@ class PurePathBase:
a drive)."""
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):
"""
Return True if this path matches the given pattern. If the pattern is
@@ -293,7 +287,7 @@ class PurePathBase:
return False
if len(path_parts) > len(pattern_parts) and path_pattern.anchor:
return False
- globber = self._globber(sep, case_sensitive)
+ globber = PathGlobber(sep, case_sensitive)
for path_part, pattern_part in zip(path_parts, pattern_parts):
match = globber.compile(pattern_part)
if match(path_part) is None:
@@ -309,9 +303,9 @@ class PurePathBase:
pattern = self.with_segments(pattern)
if case_sensitive is None:
case_sensitive = _is_case_sensitive(self.parser)
- globber = self._globber(pattern.parser.sep, case_sensitive, recursive=True)
- match = globber.compile(pattern._pattern_str)
- return match(self._pattern_str) is not None
+ globber = PathGlobber(pattern.parser.sep, case_sensitive, recursive=True)
+ match = globber.compile(str(pattern))
+ return match(str(self)) is not None
@@ -463,29 +457,25 @@ class PathBase(PurePathBase):
"""
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):
"""Iterate over this subtree and yield all existing files (of any
kind, including directories) matching the given relative pattern.
"""
if not isinstance(pattern, PurePathBase):
pattern = self.with_segments(pattern)
- anchor, parts = pattern._stack
+ anchor, parts = _explode_path(pattern)
if anchor:
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)
def rglob(self, pattern, *, case_sensitive=None, recurse_symlinks=True):
diff --git a/Lib/pathlib/_local.py b/Lib/pathlib/_local.py
index 7689c10..4897149 100644
--- a/Lib/pathlib/_local.py
+++ b/Lib/pathlib/_local.py
@@ -5,7 +5,7 @@ import os
import posixpath
import sys
from errno import EINVAL, EXDEV
-from glob import _StringGlobber
+from glob import _StringGlobber, _no_recurse_symlinks
from itertools import chain
from stat import S_ISSOCK, S_ISBLK, S_ISCHR, S_ISFIFO
from _collections_abc import Sequence
@@ -112,7 +112,6 @@ class PurePath(PurePathBase):
'_hash',
)
parser = os.path
- _globber = _StringGlobber
def __new__(cls, *args, **kwargs):
"""Construct a PurePath from one or several strings and or existing
@@ -513,13 +512,22 @@ class PurePath(PurePathBase):
from urllib.parse import quote_from_bytes
return prefix + quote_from_bytes(os.fsencode(path))
- @property
- def _pattern_str(self):
- """The path expressed as a string, for use in pattern-matching."""
+ def full_match(self, pattern, *, case_sensitive=None):
+ """
+ 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
# paths shouldn't match wildcards, so we change it to the empty string.
- path_str = str(self)
- return '' if path_str == '.' else path_str
+ path = str(self) if self.parts else ''
+ 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,
# which in turn makes Path construction slower. Register instead!
@@ -749,8 +757,18 @@ class Path(PathBase, PurePath):
kind, including directories) matching the given relative 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)
- 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)
paths = select(root)