@@ -25,6 +25,23 @@ def _is_case_sensitive(parser):
2525 return parser .normcase ('Aa' ) == 'Aa'
2626
2727
28+ def _explode_path (path ):
29+ """
30+ Split the path into a 2-tuple (anchor, parts), where *anchor* is the
31+ uppermost parent of the path (equivalent to path.parents[-1]), and
32+ *parts* is a reversed list of parts following the anchor.
33+ """
34+ split = path .parser .split
35+ path = str (path )
36+ parent , name = split (path )
37+ names = []
38+ while path != parent :
39+ names .append (name )
40+ path = parent
41+ parent , name = split (path )
42+ return path , names
43+
44+
2845class PathGlobber (_GlobberBase ):
2946 """
3047 Class providing shell-style globbing for path objects.
@@ -50,7 +67,6 @@ class PurePathBase:
5067
5168 __slots__ = ()
5269 parser = posixpath
53- _globber = PathGlobber
5470
5571 def with_segments (self , * pathsegments ):
5672 """Construct a new path object from any number of path-like objects.
@@ -82,7 +98,7 @@ def root(self):
8298 @property
8399 def anchor (self ):
84100 """The concatenation of the drive and root, or ''."""
85- return self . _stack [0 ]
101+ return _explode_path ( self ) [0 ]
86102
87103 @property
88104 def name (self ):
@@ -160,8 +176,8 @@ def relative_to(self, other, *, walk_up=False):
160176 """
161177 if not isinstance (other , PurePathBase ):
162178 other = self .with_segments (other )
163- anchor0 , parts0 = self . _stack
164- anchor1 , parts1 = other . _stack
179+ anchor0 , parts0 = _explode_path ( self )
180+ anchor1 , parts1 = _explode_path ( other )
165181 if anchor0 != anchor1 :
166182 raise ValueError (f"{ str (self )!r} and { str (other )!r} have different anchors" )
167183 while parts0 and parts1 and parts0 [- 1 ] == parts1 [- 1 ]:
@@ -183,8 +199,8 @@ def is_relative_to(self, other):
183199 """
184200 if not isinstance (other , PurePathBase ):
185201 other = self .with_segments (other )
186- anchor0 , parts0 = self . _stack
187- anchor1 , parts1 = other . _stack
202+ anchor0 , parts0 = _explode_path ( self )
203+ anchor1 , parts1 = _explode_path ( other )
188204 if anchor0 != anchor1 :
189205 return False
190206 while parts0 and parts1 and parts0 [- 1 ] == parts1 [- 1 ]:
@@ -199,7 +215,7 @@ def is_relative_to(self, other):
199215 def parts (self ):
200216 """An object providing sequence-like access to the
201217 components in the filesystem path."""
202- anchor , parts = self . _stack
218+ anchor , parts = _explode_path ( self )
203219 if anchor :
204220 parts .append (anchor )
205221 return tuple (reversed (parts ))
@@ -224,23 +240,6 @@ def __rtruediv__(self, key):
224240 except TypeError :
225241 return NotImplemented
226242
227- @property
228- def _stack (self ):
229- """
230- Split the path into a 2-tuple (anchor, parts), where *anchor* is the
231- uppermost parent of the path (equivalent to path.parents[-1]), and
232- *parts* is a reversed list of parts following the anchor.
233- """
234- split = self .parser .split
235- path = str (self )
236- parent , name = split (path )
237- names = []
238- while path != parent :
239- names .append (name )
240- path = parent
241- parent , name = split (path )
242- return path , names
243-
244243 @property
245244 def parent (self ):
246245 """The logical parent of the path."""
@@ -268,11 +267,6 @@ def is_absolute(self):
268267 a drive)."""
269268 return self .parser .isabs (str (self ))
270269
271- @property
272- def _pattern_str (self ):
273- """The path expressed as a string, for use in pattern-matching."""
274- return str (self )
275-
276270 def match (self , path_pattern , * , case_sensitive = None ):
277271 """
278272 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):
293287 return False
294288 if len (path_parts ) > len (pattern_parts ) and path_pattern .anchor :
295289 return False
296- globber = self . _globber (sep , case_sensitive )
290+ globber = PathGlobber (sep , case_sensitive )
297291 for path_part , pattern_part in zip (path_parts , pattern_parts ):
298292 match = globber .compile (pattern_part )
299293 if match (path_part ) is None :
@@ -309,9 +303,9 @@ def full_match(self, pattern, *, case_sensitive=None):
309303 pattern = self .with_segments (pattern )
310304 if case_sensitive is None :
311305 case_sensitive = _is_case_sensitive (self .parser )
312- globber = self . _globber (pattern .parser .sep , case_sensitive , recursive = True )
313- match = globber .compile (pattern . _pattern_str )
314- return match (self . _pattern_str ) is not None
306+ globber = PathGlobber (pattern .parser .sep , case_sensitive , recursive = True )
307+ match = globber .compile (str ( pattern ) )
308+ return match (str ( self ) ) is not None
315309
316310
317311
@@ -463,29 +457,25 @@ def iterdir(self):
463457 """
464458 raise NotImplementedError
465459
466- def _glob_selector (self , parts , case_sensitive , recurse_symlinks ):
467- if case_sensitive is None :
468- case_sensitive = _is_case_sensitive (self .parser )
469- case_pedantic = False
470- else :
471- # The user has expressed a case sensitivity choice, but we don't
472- # know the case sensitivity of the underlying filesystem, so we
473- # must use scandir() for everything, including non-wildcard parts.
474- case_pedantic = True
475- recursive = True if recurse_symlinks else _no_recurse_symlinks
476- globber = self ._globber (self .parser .sep , case_sensitive , case_pedantic , recursive )
477- return globber .selector (parts )
478-
479460 def glob (self , pattern , * , case_sensitive = None , recurse_symlinks = True ):
480461 """Iterate over this subtree and yield all existing files (of any
481462 kind, including directories) matching the given relative pattern.
482463 """
483464 if not isinstance (pattern , PurePathBase ):
484465 pattern = self .with_segments (pattern )
485- anchor , parts = pattern . _stack
466+ anchor , parts = _explode_path ( pattern )
486467 if anchor :
487468 raise NotImplementedError ("Non-relative patterns are unsupported" )
488- select = self ._glob_selector (parts , case_sensitive , recurse_symlinks )
469+ if case_sensitive is None :
470+ case_sensitive = _is_case_sensitive (self .parser )
471+ case_pedantic = False
472+ elif case_sensitive == _is_case_sensitive (self .parser ):
473+ case_pedantic = False
474+ else :
475+ case_pedantic = True
476+ recursive = True if recurse_symlinks else _no_recurse_symlinks
477+ globber = PathGlobber (self .parser .sep , case_sensitive , case_pedantic , recursive )
478+ select = globber .selector (parts )
489479 return select (self )
490480
491481 def rglob (self , pattern , * , case_sensitive = None , recurse_symlinks = True ):
0 commit comments