@@ -102,7 +102,7 @@ def __subclasscheck__(cls, subclass):
102102
103103class UPath (pathlib .Path , PureUPath , metaclass = UPathMeta ):
104104
105- __slots__ = ("_url" , "_kwargs" , "_closed" , "fs " )
105+ __slots__ = ("_url" , "_kwargs" , "_closed" , "_accessor " )
106106
107107 not_implemented = [
108108 "cwd" ,
@@ -158,45 +158,32 @@ def __new__(cls, *args, **kwargs):
158158 if os .name == "nt"
159159 else pathlib .PosixPath
160160 )
161- self = cls ._from_parts (args , init = False )
161+ self = cls ._from_parts (args )
162162 if not self ._flavour .is_supported :
163163 raise NotImplementedError (
164164 "cannot instantiate %r on your system" % (cls .__name__ ,)
165165 )
166- self ._init ()
167166 else :
168167 import upath .registry
169168
170169 cls = upath .registry ._registry [parsed_url .scheme ]
171170 kwargs ["_url" ] = parsed_url
172171 args_list .insert (0 , parsed_url .path )
173172 args = tuple (args_list )
174- self = cls ._from_parts_init (args , init = False )
175- self ._init (* args , ** kwargs )
173+ self = cls ._from_parts (args , ** kwargs )
176174 else :
177175 self = super ().__new__ (* args , ** kwargs )
178176 return self
179177
180- def _init (self , * args , template = None , ** kwargs ):
181- self ._closed = False
182- if not kwargs :
183- kwargs = dict (** self ._kwargs )
178+ def __getattr__ (self , item ):
179+ if item == "_accessor" :
180+ # cache the _accessor attribute on first access
181+ kw = self ._kwargs .copy ()
182+ kw .pop ("_url" , None )
183+ self ._accessor = _accessor = self ._default_accessor (self ._url , ** kw )
184+ return _accessor
184185 else :
185- self ._kwargs = dict (** kwargs )
186- self ._url = kwargs .pop ("_url" ) if kwargs .get ("_url" ) else None
187-
188- if not self ._root :
189- if not self ._parts :
190- self ._root = "/"
191- elif self ._parts [0 ] == "/" :
192- self ._root = self ._parts .pop (0 )
193- if getattr (self , "_str" , None ):
194- delattr (self , "_str" )
195- if template is not None :
196- self ._accessor = template ._accessor
197- else :
198- self ._accessor = self ._default_accessor (self ._url , * args , ** kwargs )
199- self .fs = self ._accessor ._fs
186+ raise AttributeError (item )
200187
201188 def __getattribute__ (self , item ):
202189 if item == "__class__" :
@@ -206,6 +193,21 @@ def __getattribute__(self, item):
206193 else :
207194 return super ().__getattribute__ (item )
208195
196+ def _make_child (self , args ):
197+ drv , root , parts = self ._parse_args (args , ** self ._kwargs )
198+ drv , root , parts = self ._flavour .join_parsed_parts (
199+ self ._drv , self ._root , self ._parts , drv , root , parts
200+ )
201+ return self ._from_parsed_parts (drv , root , parts , ** self ._kwargs )
202+
203+ def _make_child_relpath (self , part ):
204+ # This is an optimization used for dir walking. `part` must be
205+ # a single part relative to this path.
206+ parts = self ._parts + [part ]
207+ return self ._from_parsed_parts (
208+ self ._drv , self ._root , parts , ** self ._kwargs
209+ )
210+
209211 def _format_parsed_parts (self , drv , root , parts ):
210212 if parts :
211213 join_parts = parts [1 :] if parts [0 ] == "/" else parts
@@ -235,6 +237,19 @@ def path(self):
235237 def open (self , * args , ** kwargs ):
236238 return self ._accessor .open (self , * args , ** kwargs )
237239
240+ @property
241+ def parent (self ):
242+ """The logical parent of the path."""
243+ drv = self ._drv
244+ root = self ._root
245+ parts = self ._parts
246+ if len (parts ) == 1 and (drv or root ):
247+ return self
248+ return self ._from_parsed_parts (drv , root , parts [:- 1 ], ** self ._kwargs )
249+
250+ def stat (self ):
251+ return self ._accessor .stat (self )
252+
238253 def iterdir (self ):
239254 """Iterate over the files in this directory. Does not yield any
240255 result for the special paths '.' and '..'.
@@ -320,30 +335,50 @@ def rmdir(self, recursive=True):
320335 self ._accessor .rm (self , recursive = recursive )
321336
322337 @classmethod
323- def _from_parts_init (cls , args , init = False ):
324- return super ()._from_parts (args , init = init )
325-
326- def _from_parts (self , args , init = True ):
327- # We need to call _parse_args on the instance, so as to get the
328- # right flavour.
329- obj = object .__new__ (self .__class__ )
330- drv , root , parts = self ._parse_args (args )
338+ def _parse_args (cls , args , ** kwargs ):
339+ return super (UPath , cls )._parse_args (args )
340+
341+ @classmethod
342+ def _from_parts (cls , args , ** kwargs ):
343+ obj = object .__new__ (cls )
344+ drv , root , parts = obj ._parse_args (args , ** kwargs )
331345 obj ._drv = drv
332- obj ._root = root
333346 obj ._parts = parts
334- if init :
335- obj ._init (** self ._kwargs )
347+ obj ._closed = False
348+ obj ._kwargs = kwargs .copy ()
349+ obj ._url = kwargs .pop ("_url" , None ) or None
350+
351+ if not root :
352+ if not parts :
353+ root = "/"
354+ elif parts [0 ] == "/" :
355+ root = parts .pop (0 )
356+ obj ._root = root
357+
336358 return obj
337359
338- def _from_parsed_parts (self , drv , root , parts , init = True ):
339- obj = object .__new__ (self .__class__ )
360+ @classmethod
361+ def _from_parsed_parts (cls , drv , root , parts , ** kwargs ):
362+ obj = object .__new__ (cls )
340363 obj ._drv = drv
341- obj ._root = root
342364 obj ._parts = parts
343- if init :
344- obj ._init (** self ._kwargs )
365+ obj ._closed = False
366+ obj ._kwargs = kwargs .copy ()
367+ obj ._url = kwargs .pop ("_url" , None ) or None
368+
369+ if not root :
370+ if not parts :
371+ root = "/"
372+ elif parts [0 ] == "/" :
373+ root = parts .pop (0 )
374+ obj ._root = root
375+
345376 return obj
346377
378+ @property
379+ def fs (self ):
380+ return self ._accessor ._fs
381+
347382 def __truediv__ (self , key ):
348383 # Add `/` root if not present
349384 if len (self ._parts ) == 0 :
@@ -369,9 +404,6 @@ def __setstate__(self, state):
369404 kwargs = state ["_kwargs" ].copy ()
370405 kwargs ["_url" ] = self ._url
371406 self ._kwargs = kwargs
372- # _init needs to be called again, because when __new__ called _init,
373- # the _kwargs were not yet set
374- self ._init ()
375407
376408 def __reduce__ (self ):
377409 kwargs = self ._kwargs .copy ()
0 commit comments