1414from glob import _PathGlobber
1515from pathlib ._os import magic_open , ensure_distinct_paths , ensure_different_files , copyfileobj
1616from pathlib import PurePath , Path
17- from typing import Optional , Protocol , runtime_checkable
17+ from typing import (
18+ Any , BinaryIO , Callable , Generator , Optional , Protocol , Sequence , TypeVar , Union ,
19+ runtime_checkable ,
20+ )
21+
22+
23+ _JP = TypeVar ("_JP" , bound = "_JoinablePath" )
24+ _RP = TypeVar ("_RP" , bound = "_ReadablePath" )
25+ _WP = TypeVar ("_WP" , bound = "_WritablePath" )
1826
1927
2028def _explode_path (path ):
@@ -72,38 +80,38 @@ class _JoinablePath(ABC):
7280
7381 @property
7482 @abstractmethod
75- def parser (self ):
83+ def parser (self ) -> _PathParser :
7684 """Implementation of pathlib._types.Parser used for low-level path
7785 parsing and manipulation.
7886 """
7987 raise NotImplementedError
8088
8189 @abstractmethod
82- def with_segments (self , * pathsegments ) :
90+ def with_segments (self : _JP , * pathsegments : Union [ _JP , str ]) -> _JP :
8391 """Construct a new path object from any number of path-like objects.
8492 Subclasses may override this method to customize how new path objects
8593 are created from methods like `iterdir()`.
8694 """
8795 raise NotImplementedError
8896
8997 @abstractmethod
90- def __str__ (self ):
98+ def __str__ (self ) -> str :
9199 """Return the string representation of the path, suitable for
92100 passing to system calls."""
93101 raise NotImplementedError
94102
95103 @property
96- def anchor (self ):
104+ def anchor (self ) -> str :
97105 """The concatenation of the drive and root, or ''."""
98106 return _explode_path (self )[0 ]
99107
100108 @property
101- def name (self ):
109+ def name (self ) -> str :
102110 """The final path component, if any."""
103111 return self .parser .split (str (self ))[1 ]
104112
105113 @property
106- def suffix (self ):
114+ def suffix (self ) -> str :
107115 """
108116 The final component's last suffix, if any.
109117
@@ -112,7 +120,7 @@ def suffix(self):
112120 return self .parser .splitext (self .name )[1 ]
113121
114122 @property
115- def suffixes (self ):
123+ def suffixes (self ) -> Sequence [ str ] :
116124 """
117125 A list of the final component's suffixes, if any.
118126
@@ -127,11 +135,11 @@ def suffixes(self):
127135 return suffixes [::- 1 ]
128136
129137 @property
130- def stem (self ):
138+ def stem (self ) -> str :
131139 """The final path component, minus its last suffix."""
132140 return self .parser .splitext (self .name )[0 ]
133141
134- def with_name (self , name ) :
142+ def with_name (self : _JP , name : str ) -> _JP :
135143 """Return a new path with the file name changed."""
136144 split = self .parser .split
137145 if split (name )[0 ]:
@@ -140,7 +148,7 @@ def with_name(self, name):
140148 path = path .removesuffix (split (path )[1 ]) + name
141149 return self .with_segments (path )
142150
143- def with_stem (self , stem ) :
151+ def with_stem (self : _JP , stem : str ) -> _JP :
144152 """Return a new path with the stem changed."""
145153 suffix = self .suffix
146154 if not suffix :
@@ -151,7 +159,7 @@ def with_stem(self, stem):
151159 else :
152160 return self .with_name (stem + suffix )
153161
154- def with_suffix (self , suffix ) :
162+ def with_suffix (self : _JP , suffix : str ) -> _JP :
155163 """Return a new path with the file suffix changed. If the path
156164 has no suffix, add given suffix. If the given suffix is an empty
157165 string, remove the suffix from the path.
@@ -166,36 +174,36 @@ def with_suffix(self, suffix):
166174 return self .with_name (stem + suffix )
167175
168176 @property
169- def parts (self ):
177+ def parts (self ) -> Sequence [ str ] :
170178 """An object providing sequence-like access to the
171179 components in the filesystem path."""
172180 anchor , parts = _explode_path (self )
173181 if anchor :
174182 parts .append (anchor )
175183 return tuple (reversed (parts ))
176184
177- def joinpath (self , * pathsegments ) :
185+ def joinpath (self : _JP , * pathsegments : Union [ _JP , str ]) -> _JP :
178186 """Combine this path with one or several arguments, and return a
179187 new path representing either a subpath (if all arguments are relative
180188 paths) or a totally different path (if one of the arguments is
181189 anchored).
182190 """
183191 return self .with_segments (str (self ), * pathsegments )
184192
185- def __truediv__ (self , key ) :
193+ def __truediv__ (self : _JP , key : Union [ _JP , str ]) -> _JP :
186194 try :
187195 return self .with_segments (str (self ), key )
188196 except TypeError :
189197 return NotImplemented
190198
191- def __rtruediv__ (self , key ) :
199+ def __rtruediv__ (self : _JP , key : Union [ _JP , str ]) -> _JP :
192200 try :
193201 return self .with_segments (key , str (self ))
194202 except TypeError :
195203 return NotImplemented
196204
197205 @property
198- def parent (self ) :
206+ def parent (self : _JP ) -> _JP :
199207 """The logical parent of the path."""
200208 path = str (self )
201209 parent = self .parser .split (path )[0 ]
@@ -204,7 +212,7 @@ def parent(self):
204212 return self
205213
206214 @property
207- def parents (self ) :
215+ def parents (self : _JP ) -> Sequence [ _JP ] :
208216 """A sequence of this path's logical parents."""
209217 split = self .parser .split
210218 path = str (self )
@@ -216,7 +224,7 @@ def parents(self):
216224 parent = split (path )[0 ]
217225 return tuple (parents )
218226
219- def full_match (self , pattern ) :
227+ def full_match (self : _JP , pattern : Union [ _JP , str ]) -> bool :
220228 """
221229 Return True if this path matches the given glob-style pattern. The
222230 pattern is matched against the entire path.
@@ -240,45 +248,50 @@ class _ReadablePath(_JoinablePath):
240248
241249 @property
242250 @abstractmethod
243- def info (self ):
251+ def info (self ) -> PathInfo :
244252 """
245253 A PathInfo object that exposes the file type and other file attributes
246254 of this path.
247255 """
248256 raise NotImplementedError
249257
250258 @abstractmethod
251- def __open_rb__ (self , buffering = - 1 ):
259+ def __open_rb__ (self , buffering : int = - 1 ) -> BinaryIO :
252260 """
253261 Open the file pointed to by this path for reading in binary mode and
254262 return a file object, like open(mode='rb').
255263 """
256264 raise NotImplementedError
257265
258- def read_bytes (self ):
266+ def read_bytes (self ) -> bytes :
259267 """
260268 Open the file in bytes mode, read it, and close the file.
261269 """
262270 with magic_open (self , mode = 'rb' , buffering = 0 ) as f :
263271 return f .read ()
264272
265- def read_text (self , encoding = None , errors = None , newline = None ):
273+ def read_text (
274+ self ,
275+ encoding : Optional [str ] = None ,
276+ errors : Optional [str ] = None ,
277+ newline : Optional [str ] = None ,
278+ ) -> str :
266279 """
267280 Open the file in text mode, read it, and close the file.
268281 """
269282 with magic_open (self , mode = 'r' , encoding = encoding , errors = errors , newline = newline ) as f :
270283 return f .read ()
271284
272285 @abstractmethod
273- def iterdir (self ) :
286+ def iterdir (self : _RP ) -> Generator [ _RP , None , None ] :
274287 """Yield path objects of the directory contents.
275288
276289 The children are yielded in arbitrary order, and the
277290 special entries '.' and '..' are not included.
278291 """
279292 raise NotImplementedError
280293
281- def glob (self , pattern , * , recurse_symlinks = True ):
294+ def glob (self : _RP , pattern : Union [ _RP , str ], * , recurse_symlinks : bool = True ) -> Generator [ _RP , None , None ] :
282295 """Iterate over this subtree and yield all existing files (of any
283296 kind, including directories) matching the given relative pattern.
284297 """
@@ -294,7 +307,12 @@ def glob(self, pattern, *, recurse_symlinks=True):
294307 select = globber .selector (parts )
295308 return select (self .joinpath ('' ))
296309
297- def walk (self , top_down = True , on_error = None , follow_symlinks = False ):
310+ def walk (
311+ self : _RP ,
312+ top_down : bool = True ,
313+ on_error : Optional [Callable [[Exception ], None ]] = None ,
314+ follow_symlinks : bool = False ,
315+ ) -> Generator [tuple [_RP , list [str ], list [str ]], None , None ]:
298316 """Walk the directory tree from this directory, similar to os.walk()."""
299317 paths = [self ]
300318 while paths :
@@ -326,13 +344,13 @@ def walk(self, top_down=True, on_error=None, follow_symlinks=False):
326344 paths += [path .joinpath (d ) for d in reversed (dirnames )]
327345
328346 @abstractmethod
329- def readlink (self ) :
347+ def readlink (self : _RP ) -> _RP :
330348 """
331349 Return the path to which the symbolic link points.
332350 """
333351 raise NotImplementedError
334352
335- def copy (self , target , ** kwargs ) :
353+ def copy (self , target : _WP , ** kwargs : Any ) -> _WP :
336354 """
337355 Recursively copy this file or directory tree to the given destination.
338356 """
@@ -346,7 +364,7 @@ def copy(self, target, **kwargs):
346364 copy_to_target (self , ** kwargs )
347365 return target .joinpath () # Empty join to ensure fresh metadata.
348366
349- def copy_into (self , target_dir , ** kwargs ) :
367+ def copy_into (self , target_dir : _WP , ** kwargs : Any ) -> _WP :
350368 """
351369 Copy this file or directory tree into the given existing directory.
352370 """
@@ -370,29 +388,29 @@ class _WritablePath(_JoinablePath):
370388 __slots__ = ()
371389
372390 @abstractmethod
373- def symlink_to (self , target , target_is_directory = False ):
391+ def symlink_to (self : _WP , target : _WP , target_is_directory : bool = False ) -> None :
374392 """
375393 Make this path a symlink pointing to the target path.
376394 Note the order of arguments (link, target) is the reverse of os.symlink.
377395 """
378396 raise NotImplementedError
379397
380398 @abstractmethod
381- def mkdir (self ):
399+ def mkdir (self ) -> None :
382400 """
383401 Create a new directory at this given path.
384402 """
385403 raise NotImplementedError
386404
387405 @abstractmethod
388- def __open_wb__ (self , buffering = - 1 ):
406+ def __open_wb__ (self , buffering : int = - 1 ) -> BinaryIO :
389407 """
390408 Open the file pointed to by this path for writing in binary mode and
391409 return a file object, like open(mode='wb').
392410 """
393411 raise NotImplementedError
394412
395- def write_bytes (self , data ) :
413+ def write_bytes (self , data : bytes ) -> int :
396414 """
397415 Open the file in bytes mode, write to it, and close the file.
398416 """
@@ -401,7 +419,13 @@ def write_bytes(self, data):
401419 with magic_open (self , mode = 'wb' ) as f :
402420 return f .write (view )
403421
404- def write_text (self , data , encoding = None , errors = None , newline = None ):
422+ def write_text (
423+ self ,
424+ data : str ,
425+ encoding : Optional [str ] = None ,
426+ errors : Optional [str ] = None ,
427+ newline : Optional [str ] = None ,
428+ ) -> int :
405429 """
406430 Open the file in text mode, write to it, and close the file.
407431 """
@@ -411,7 +435,7 @@ def write_text(self, data, encoding=None, errors=None, newline=None):
411435 with magic_open (self , mode = 'w' , encoding = encoding , errors = errors , newline = newline ) as f :
412436 return f .write (data )
413437
414- def _copy_from (self , source , follow_symlinks = True ):
438+ def _copy_from (self , source : _ReadablePath , follow_symlinks : bool = True ) -> None :
415439 """
416440 Recursively copy the given path to this path.
417441 """
0 commit comments