1111
1212
1313from abc import ABC , abstractmethod
14- from glob import _PathGlobber
14+ from glob import _GlobberBase
1515from io import text_encoding
16- from pathlib ._os import magic_open , ensure_distinct_paths , ensure_different_files , copyfileobj
16+ from pathlib ._os import (magic_open , vfspath , ensure_distinct_paths ,
17+ ensure_different_files , copyfileobj )
1718from pathlib import PurePath , Path
1819from typing import Optional , Protocol , runtime_checkable
1920
@@ -60,6 +61,25 @@ def is_file(self, *, follow_symlinks: bool = True) -> bool: ...
6061 def is_symlink (self ) -> bool : ...
6162
6263
64+ class _PathGlobber (_GlobberBase ):
65+ """Provides shell-style pattern matching and globbing for ReadablePath.
66+ """
67+
68+ @staticmethod
69+ def lexists (path ):
70+ return path .info .exists (follow_symlinks = False )
71+
72+ @staticmethod
73+ def scandir (path ):
74+ return ((child .info , child .name , child ) for child in path .iterdir ())
75+
76+ @staticmethod
77+ def concat_path (path , text ):
78+ return path .with_segments (vfspath (path ) + text )
79+
80+ stringify_path = staticmethod (vfspath )
81+
82+
6383class _JoinablePath (ABC ):
6484 """Abstract base class for pure path objects.
6585
@@ -86,20 +106,19 @@ def with_segments(self, *pathsegments):
86106 raise NotImplementedError
87107
88108 @abstractmethod
89- def __str__ (self ):
90- """Return the string representation of the path, suitable for
91- passing to system calls."""
109+ def __vfspath__ (self ):
110+ """Return the string representation of the path."""
92111 raise NotImplementedError
93112
94113 @property
95114 def anchor (self ):
96115 """The concatenation of the drive and root, or ''."""
97- return _explode_path (str (self ), self .parser .split )[0 ]
116+ return _explode_path (vfspath (self ), self .parser .split )[0 ]
98117
99118 @property
100119 def name (self ):
101120 """The final path component, if any."""
102- return self .parser .split (str (self ))[1 ]
121+ return self .parser .split (vfspath (self ))[1 ]
103122
104123 @property
105124 def suffix (self ):
@@ -135,7 +154,7 @@ def with_name(self, name):
135154 split = self .parser .split
136155 if split (name )[0 ]:
137156 raise ValueError (f"Invalid name { name !r} " )
138- path = str (self )
157+ path = vfspath (self )
139158 path = path .removesuffix (split (path )[1 ]) + name
140159 return self .with_segments (path )
141160
@@ -168,7 +187,7 @@ def with_suffix(self, suffix):
168187 def parts (self ):
169188 """An object providing sequence-like access to the
170189 components in the filesystem path."""
171- anchor , parts = _explode_path (str (self ), self .parser .split )
190+ anchor , parts = _explode_path (vfspath (self ), self .parser .split )
172191 if anchor :
173192 parts .append (anchor )
174193 return tuple (reversed (parts ))
@@ -179,24 +198,24 @@ def joinpath(self, *pathsegments):
179198 paths) or a totally different path (if one of the arguments is
180199 anchored).
181200 """
182- return self .with_segments (str (self ), * pathsegments )
201+ return self .with_segments (vfspath (self ), * pathsegments )
183202
184203 def __truediv__ (self , key ):
185204 try :
186- return self .with_segments (str (self ), key )
205+ return self .with_segments (vfspath (self ), key )
187206 except TypeError :
188207 return NotImplemented
189208
190209 def __rtruediv__ (self , key ):
191210 try :
192- return self .with_segments (key , str (self ))
211+ return self .with_segments (key , vfspath (self ))
193212 except TypeError :
194213 return NotImplemented
195214
196215 @property
197216 def parent (self ):
198217 """The logical parent of the path."""
199- path = str (self )
218+ path = vfspath (self )
200219 parent = self .parser .split (path )[0 ]
201220 if path != parent :
202221 return self .with_segments (parent )
@@ -206,7 +225,7 @@ def parent(self):
206225 def parents (self ):
207226 """A sequence of this path's logical parents."""
208227 split = self .parser .split
209- path = str (self )
228+ path = vfspath (self )
210229 parent = split (path )[0 ]
211230 parents = []
212231 while path != parent :
@@ -223,7 +242,7 @@ def full_match(self, pattern):
223242 case_sensitive = self .parser .normcase ('Aa' ) == 'Aa'
224243 globber = _PathGlobber (self .parser .sep , case_sensitive , recursive = True )
225244 match = globber .compile (pattern , altsep = self .parser .altsep )
226- return match (str (self )) is not None
245+ return match (vfspath (self )) is not None
227246
228247
229248class _ReadablePath (_JoinablePath ):
@@ -412,7 +431,7 @@ def _copy_from(self, source, follow_symlinks=True):
412431 while stack :
413432 src , dst = stack .pop ()
414433 if not follow_symlinks and src .info .is_symlink ():
415- dst .symlink_to (str (src .readlink ()), src .info .is_dir ())
434+ dst .symlink_to (vfspath (src .readlink ()), src .info .is_dir ())
416435 elif src .info .is_dir ():
417436 children = src .iterdir ()
418437 dst .mkdir ()
0 commit comments