Skip to content

Commit 15614a6

Browse files
committed
GH-127807: pathlib ABCs: remove a few private attributes
From `PurePathBase` delete `_stack` and `_pattern_str`, and from `PathBase` delete `_glob_selector`.
1 parent 12b4f1a commit 15614a6

File tree

2 files changed

+62
-52
lines changed

2 files changed

+62
-52
lines changed

Lib/pathlib/_abc.py

Lines changed: 36 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,23 @@ def _is_case_sensitive(parser):
3434
return parser.normcase('Aa') == 'Aa'
3535

3636

37+
def _parse_path(path):
38+
"""
39+
Split the path into a 2-tuple (anchor, parts), where *anchor* is the
40+
uppermost parent of the path (equivalent to path.parents[-1]), and
41+
*parts* is a reversed list of parts following the anchor.
42+
"""
43+
split = path.parser.split
44+
path = str(path)
45+
parent, name = split(path)
46+
names = []
47+
while path != parent:
48+
names.append(name)
49+
path = parent
50+
parent, name = split(path)
51+
return path, names
52+
53+
3754
class PathGlobber(_GlobberBase):
3855
"""
3956
Class providing shell-style globbing for path objects.
@@ -115,7 +132,7 @@ def root(self):
115132
@property
116133
def anchor(self):
117134
"""The concatenation of the drive and root, or ''."""
118-
return self._stack[0]
135+
return _parse_path(self)[0]
119136

120137
@property
121138
def name(self):
@@ -193,8 +210,8 @@ def relative_to(self, other, *, walk_up=False):
193210
"""
194211
if not isinstance(other, PurePathBase):
195212
other = self.with_segments(other)
196-
anchor0, parts0 = self._stack
197-
anchor1, parts1 = other._stack
213+
anchor0, parts0 = _parse_path(self)
214+
anchor1, parts1 = _parse_path(other)
198215
if anchor0 != anchor1:
199216
raise ValueError(f"{str(self)!r} and {str(other)!r} have different anchors")
200217
while parts0 and parts1 and parts0[-1] == parts1[-1]:
@@ -216,8 +233,8 @@ def is_relative_to(self, other):
216233
"""
217234
if not isinstance(other, PurePathBase):
218235
other = self.with_segments(other)
219-
anchor0, parts0 = self._stack
220-
anchor1, parts1 = other._stack
236+
anchor0, parts0 = _parse_path(self)
237+
anchor1, parts1 = _parse_path(other)
221238
if anchor0 != anchor1:
222239
return False
223240
while parts0 and parts1 and parts0[-1] == parts1[-1]:
@@ -232,7 +249,7 @@ def is_relative_to(self, other):
232249
def parts(self):
233250
"""An object providing sequence-like access to the
234251
components in the filesystem path."""
235-
anchor, parts = self._stack
252+
anchor, parts = _parse_path(self)
236253
if anchor:
237254
parts.append(anchor)
238255
return tuple(reversed(parts))
@@ -257,23 +274,6 @@ def __rtruediv__(self, key):
257274
except TypeError:
258275
return NotImplemented
259276

260-
@property
261-
def _stack(self):
262-
"""
263-
Split the path into a 2-tuple (anchor, parts), where *anchor* is the
264-
uppermost parent of the path (equivalent to path.parents[-1]), and
265-
*parts* is a reversed list of parts following the anchor.
266-
"""
267-
split = self.parser.split
268-
path = str(self)
269-
parent, name = split(path)
270-
names = []
271-
while path != parent:
272-
names.append(name)
273-
path = parent
274-
parent, name = split(path)
275-
return path, names
276-
277277
@property
278278
def parent(self):
279279
"""The logical parent of the path."""
@@ -301,11 +301,6 @@ def is_absolute(self):
301301
a drive)."""
302302
return self.parser.isabs(str(self))
303303

304-
@property
305-
def _pattern_str(self):
306-
"""The path expressed as a string, for use in pattern-matching."""
307-
return str(self)
308-
309304
def match(self, path_pattern, *, case_sensitive=None):
310305
"""
311306
Return True if this path matches the given pattern. If the pattern is
@@ -343,8 +338,8 @@ def full_match(self, pattern, *, case_sensitive=None):
343338
if case_sensitive is None:
344339
case_sensitive = _is_case_sensitive(self.parser)
345340
globber = self._globber(pattern.parser.sep, case_sensitive, recursive=True)
346-
match = globber.compile(pattern._pattern_str)
347-
return match(self._pattern_str) is not None
341+
match = globber.compile(str(pattern))
342+
return match(str(self)) is not None
348343

349344

350345

@@ -500,29 +495,25 @@ def iterdir(self):
500495
"""
501496
raise UnsupportedOperation(self._unsupported_msg('iterdir()'))
502497

503-
def _glob_selector(self, parts, case_sensitive, recurse_symlinks):
504-
if case_sensitive is None:
505-
case_sensitive = _is_case_sensitive(self.parser)
506-
case_pedantic = False
507-
else:
508-
# The user has expressed a case sensitivity choice, but we don't
509-
# know the case sensitivity of the underlying filesystem, so we
510-
# must use scandir() for everything, including non-wildcard parts.
511-
case_pedantic = True
512-
recursive = True if recurse_symlinks else _no_recurse_symlinks
513-
globber = self._globber(self.parser.sep, case_sensitive, case_pedantic, recursive)
514-
return globber.selector(parts)
515-
516498
def glob(self, pattern, *, case_sensitive=None, recurse_symlinks=True):
517499
"""Iterate over this subtree and yield all existing files (of any
518500
kind, including directories) matching the given relative pattern.
519501
"""
520502
if not isinstance(pattern, PurePathBase):
521503
pattern = self.with_segments(pattern)
522-
anchor, parts = pattern._stack
504+
anchor, parts = _parse_path(pattern)
523505
if anchor:
524506
raise NotImplementedError("Non-relative patterns are unsupported")
525-
select = self._glob_selector(parts, case_sensitive, recurse_symlinks)
507+
if case_sensitive is None:
508+
case_sensitive = _is_case_sensitive(self.parser)
509+
case_pedantic = False
510+
elif case_sensitive == _is_case_sensitive(self.parser):
511+
case_pedantic = False
512+
else:
513+
case_pedantic = True
514+
recursive = True if recurse_symlinks else _no_recurse_symlinks
515+
globber = self._globber(self.parser.sep, case_sensitive, case_pedantic, recursive)
516+
select = globber.selector(parts)
526517
return select(self)
527518

528519
def rglob(self, pattern, *, case_sensitive=None, recurse_symlinks=True):

Lib/pathlib/_local.py

Lines changed: 26 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
import posixpath
66
import sys
77
from errno import EINVAL, EXDEV
8-
from glob import _StringGlobber
8+
from glob import _StringGlobber, _no_recurse_symlinks
99
from itertools import chain
1010
from stat import S_ISSOCK, S_ISBLK, S_ISCHR, S_ISFIFO
1111
from _collections_abc import Sequence
@@ -483,13 +483,22 @@ def as_uri(self):
483483
from urllib.parse import quote_from_bytes
484484
return prefix + quote_from_bytes(os.fsencode(path))
485485

486-
@property
487-
def _pattern_str(self):
488-
"""The path expressed as a string, for use in pattern-matching."""
486+
def full_match(self, pattern, *, case_sensitive=None):
487+
"""
488+
Return True if this path matches the given glob-style pattern. The
489+
pattern is matched against the entire path.
490+
"""
491+
if not isinstance(pattern, PurePathBase):
492+
pattern = self.with_segments(pattern)
493+
if case_sensitive is None:
494+
case_sensitive = self.parser is posixpath
495+
489496
# The string representation of an empty path is a single dot ('.'). Empty
490497
# paths shouldn't match wildcards, so we change it to the empty string.
491-
path_str = str(self)
492-
return '' if path_str == '.' else path_str
498+
path = str(self) if self.parts else ''
499+
pattern = str(pattern) if pattern.parts else ''
500+
globber = self._globber(self.parser.sep, case_sensitive, recursive=True)
501+
return globber.compile(pattern)(path) is not None
493502

494503
# Subclassing os.PathLike makes isinstance() checks slower,
495504
# which in turn makes Path construction slower. Register instead!
@@ -724,8 +733,18 @@ def glob(self, pattern, *, case_sensitive=None, recurse_symlinks=False):
724733
kind, including directories) matching the given relative pattern.
725734
"""
726735
sys.audit("pathlib.Path.glob", self, pattern)
736+
if case_sensitive is None:
737+
case_sensitive = self.parser is posixpath
738+
case_pedantic = False
739+
else:
740+
# The user has expressed a case sensitivity choice, but we don't
741+
# know the case sensitivity of the underlying filesystem, so we
742+
# must use scandir() for everything, including non-wildcard parts.
743+
case_pedantic = True
727744
parts = self._parse_pattern(pattern)
728-
select = self._glob_selector(parts[::-1], case_sensitive, recurse_symlinks)
745+
recursive = True if recurse_symlinks else _no_recurse_symlinks
746+
globber = self._globber(self.parser.sep, case_sensitive, case_pedantic, recursive)
747+
select = globber.selector(parts[::-1])
729748
root = str(self)
730749
paths = select(root)
731750

0 commit comments

Comments
 (0)