@@ -198,14 +198,13 @@ def __repr__(self):
198198 return "<{}.parents>" .format (type (self ._path ).__name__ )
199199
200200
201- class PurePath :
202- """Base class for manipulating paths without I/O .
201+ class _PurePathBase :
202+ """Base class for pure path objects .
203203
204- PurePath represents a filesystem path and offers operations which
205- don't imply any actual filesystem I/O. Depending on your system,
206- instantiating a PurePath will return either a PurePosixPath or a
207- PureWindowsPath object. You can also instantiate either of these classes
208- directly, regardless of your system.
204+ This class *does not* provide several magic methods that are defined in
205+ its subclass PurePath. They are: __fspath__, __bytes__, __reduce__,
206+ __hash__, __eq__, __lt__, __le__, __gt__, __ge__. Its initializer and path
207+ joining methods accept only strings, not os.PathLike objects more broadly.
209208 """
210209
211210 __slots__ = (
@@ -227,29 +226,17 @@ class PurePath:
227226 # for the first time. It's used to implement `_str_normcase`
228227 '_str' ,
229228
230- # The `_str_normcase_cached` slot stores the string path with
231- # normalized case. It is set when the `_str_normcase` property is
232- # accessed for the first time. It's used to implement `__eq__()`
233- # `__hash__()`, and `_parts_normcase`
234- '_str_normcase_cached' ,
235-
236- # The `_parts_normcase_cached` slot stores the case-normalized
237- # string path after splitting on path separators. It's set when the
238- # `_parts_normcase` property is accessed for the first time. It's used
239- # to implement comparison methods like `__lt__()`.
240- '_parts_normcase_cached' ,
241-
242- # The `_hash` slot stores the hash of the case-normalized string
243- # path. It's set when `__hash__()` is called for the first time.
244- '_hash' ,
245-
246229 # The '_resolving' slot stores a boolean indicating whether the path
247230 # is being processed by `_PathBase.resolve()`. This prevents duplicate
248231 # work from occurring when `resolve()` calls `stat()` or `readlink()`.
249232 '_resolving' ,
250233 )
251234 pathmod = os .path
252235
236+ def __init__ (self , * paths ):
237+ self ._raw_paths = paths
238+ self ._resolving = False
239+
253240 def with_segments (self , * pathsegments ):
254241 """Construct a new path object from any number of path-like objects.
255242 Subclasses may override this method to customize how new path objects
@@ -444,7 +431,7 @@ def relative_to(self, other, /, *_deprecated, walk_up=False):
444431 warnings ._deprecated ("pathlib.PurePath.relative_to(*args)" , msg ,
445432 remove = (3 , 14 ))
446433 other = self .with_segments (other , * _deprecated )
447- elif not isinstance (other , PurePath ):
434+ elif not isinstance (other , _PurePathBase ):
448435 other = self .with_segments (other )
449436 for step , path in enumerate (chain ([other ], other .parents )):
450437 if path == self or path in self .parents :
@@ -468,7 +455,7 @@ def is_relative_to(self, other, /, *_deprecated):
468455 warnings ._deprecated ("pathlib.PurePath.is_relative_to(*args)" ,
469456 msg , remove = (3 , 14 ))
470457 other = self .with_segments (other , * _deprecated )
471- elif not isinstance (other , PurePath ):
458+ elif not isinstance (other , _PurePathBase ):
472459 other = self .with_segments (other )
473460 return other == self or other in self .parents
474461
@@ -487,7 +474,7 @@ def joinpath(self, *pathsegments):
487474 paths) or a totally different path (if one of the arguments is
488475 anchored).
489476 """
490- return self .with_segments (self , * pathsegments )
477+ return self .with_segments (* self . _raw_paths , * pathsegments )
491478
492479 def __truediv__ (self , key ):
493480 try :
@@ -497,7 +484,7 @@ def __truediv__(self, key):
497484
498485 def __rtruediv__ (self , key ):
499486 try :
500- return self .with_segments (key , self )
487+ return self .with_segments (key , * self . _raw_paths )
501488 except TypeError :
502489 return NotImplemented
503490
@@ -555,7 +542,7 @@ def match(self, path_pattern, *, case_sensitive=None):
555542 """
556543 Return True if this path matches the given pattern.
557544 """
558- if not isinstance (path_pattern , PurePath ):
545+ if not isinstance (path_pattern , _PurePathBase ):
559546 path_pattern = self .with_segments (path_pattern )
560547 if case_sensitive is None :
561548 case_sensitive = _is_case_sensitive (self .pathmod )
@@ -570,6 +557,35 @@ def match(self, path_pattern, *, case_sensitive=None):
570557 match = _compile_pattern (pattern_str , sep , case_sensitive )
571558 return match (str (self )) is not None
572559
560+
561+ class PurePath (_PurePathBase ):
562+ """Base class for manipulating paths without I/O.
563+
564+ PurePath represents a filesystem path and offers operations which
565+ don't imply any actual filesystem I/O. Depending on your system,
566+ instantiating a PurePath will return either a PurePosixPath or a
567+ PureWindowsPath object. You can also instantiate either of these classes
568+ directly, regardless of your system.
569+ """
570+
571+ __slots__ = (
572+ # The `_str_normcase_cached` slot stores the string path with
573+ # normalized case. It is set when the `_str_normcase` property is
574+ # accessed for the first time. It's used to implement `__eq__()`
575+ # `__hash__()`, and `_parts_normcase`
576+ '_str_normcase_cached' ,
577+
578+ # The `_parts_normcase_cached` slot stores the case-normalized
579+ # string path after splitting on path separators. It's set when the
580+ # `_parts_normcase` property is accessed for the first time. It's used
581+ # to implement comparison methods like `__lt__()`.
582+ '_parts_normcase_cached' ,
583+
584+ # The `_hash` slot stores the hash of the case-normalized string
585+ # path. It's set when `__hash__()` is called for the first time.
586+ '_hash' ,
587+ )
588+
573589 def __new__ (cls , * args , ** kwargs ):
574590 """Construct a PurePath from one or several strings and or existing
575591 PurePath objects. The strings and path objects are combined so as
@@ -600,8 +616,7 @@ def __init__(self, *args):
600616 "object where __fspath__ returns a str, "
601617 f"not { type (path ).__name__ !r} " )
602618 paths .append (path )
603- self ._raw_paths = paths
604- self ._resolving = False
619+ super ().__init__ (* paths )
605620
606621 def __reduce__ (self ):
607622 # Using the parts tuple helps share interned path parts
@@ -719,7 +734,7 @@ class PureWindowsPath(PurePath):
719734# Filesystem-accessing classes
720735
721736
722- class _PathBase (PurePath ):
737+ class _PathBase (_PurePathBase ):
723738 """Base class for concrete path objects.
724739
725740 This class provides dummy implementations for many methods that derived
@@ -733,8 +748,6 @@ class _PathBase(PurePath):
733748 such as paths in archive files or on remote storage systems.
734749 """
735750 __slots__ = ()
736- __bytes__ = None
737- __fspath__ = None # virtual paths have no local file system representation
738751
739752 @classmethod
740753 def _unsupported (cls , method_name ):
@@ -1341,7 +1354,7 @@ def as_uri(self):
13411354 self ._unsupported ("as_uri" )
13421355
13431356
1344- class Path (_PathBase ):
1357+ class Path (_PathBase , PurePath ):
13451358 """PurePath subclass that can make system calls.
13461359
13471360 Path represents a filesystem path but unlike PurePath, also offers
@@ -1351,8 +1364,6 @@ class Path(_PathBase):
13511364 but cannot instantiate a WindowsPath on a POSIX system or vice versa.
13521365 """
13531366 __slots__ = ()
1354- __bytes__ = PurePath .__bytes__
1355- __fspath__ = PurePath .__fspath__
13561367 as_uri = PurePath .as_uri
13571368
13581369 def __init__ (self , * args , ** kwargs ):
0 commit comments