@@ -363,47 +363,46 @@ def copy_info(info, target, follow_symlinks=True):
363363 raise
364364
365365
366- class _PathInfoBase :
367- __slots__ = ('_path' , '_stat_result' , '_lstat_result' )
366+ _STAT_RESULT_ERROR = [] # falsy sentinel indicating stat() failed.
368367
369- def __init__ (self , path ):
370- self ._path = str (path )
368+
369+ class LocalPathInfo :
370+ """Implementation of pathlib.types.PathInfo that provides information
371+ about local filesystem paths. Don't try to construct it yourself."""
372+ __slots__ = ('_path' , '_dir_entry' , '_stat_result' , '_lstat_result' )
373+
374+ def __init__ (self , path , dir_entry = None ):
375+ self ._path = path
376+ self ._dir_entry = dir_entry
377+ self ._stat_result = None
378+ self ._lstat_result = None
371379
372380 def __repr__ (self ):
373381 path_type = "WindowsPath" if os .name == "nt" else "PosixPath"
374382 return f"<{ path_type } .info>"
375383
376- def _stat (self , * , follow_symlinks = True , ignore_errors = False ):
377- """Return the status as an os.stat_result, or None if stat() fails and
378- ignore_errors is true."""
384+ def _stat (self , * , follow_symlinks = True ):
385+ """Return the status as an os.stat_result."""
379386 if follow_symlinks :
380- try :
381- result = self ._stat_result
382- except AttributeError :
383- pass
384- else :
385- if ignore_errors or result is not None :
386- return result
387- try :
388- self ._stat_result = os .stat (self ._path )
389- except (OSError , ValueError ):
390- self ._stat_result = None
391- if not ignore_errors :
387+ if not self ._stat_result :
388+ try :
389+ if self ._dir_entry :
390+ self ._stat_result = self ._dir_entry .stat ()
391+ else :
392+ self ._stat_result = os .stat (self ._path )
393+ except (OSError , ValueError ):
394+ self ._stat_result = _STAT_RESULT_ERROR
392395 raise
393396 return self ._stat_result
394397 else :
395- try :
396- result = self ._lstat_result
397- except AttributeError :
398- pass
399- else :
400- if ignore_errors or result is not None :
401- return result
402- try :
403- self ._lstat_result = os .lstat (self ._path )
404- except (OSError , ValueError ):
405- self ._lstat_result = None
406- if not ignore_errors :
398+ if not self ._lstat_result :
399+ try :
400+ if self ._dir_entry :
401+ self ._stat_result = self ._dir_entry .stat (follow_symlinks = False )
402+ else :
403+ self ._lstat_result = os .lstat (self ._path )
404+ except (OSError , ValueError ):
405+ self ._lstat_result = _STAT_RESULT_ERROR
407406 raise
408407 return self ._lstat_result
409408
@@ -442,141 +441,75 @@ def _xattrs(self, *, follow_symlinks=True):
442441 raise
443442 return []
444443
445-
446- class _WindowsPathInfo (_PathInfoBase ):
447- """Implementation of pathlib.types.PathInfo that provides status
448- information for Windows paths. Don't try to construct it yourself."""
449- __slots__ = ('_exists' , '_is_dir' , '_is_file' , '_is_symlink' )
450-
451444 def exists (self , * , follow_symlinks = True ):
452445 """Whether this path exists."""
453- if not follow_symlinks and self .is_symlink ():
454- return True
455- try :
456- return self ._exists
457- except AttributeError :
458- if os .path .exists (self ._path ):
459- self ._exists = True
460- return True
461- else :
462- self ._exists = self ._is_dir = self ._is_file = False
446+ if follow_symlinks :
447+ if self ._stat_result is _STAT_RESULT_ERROR :
463448 return False
464-
465- def is_dir (self , * , follow_symlinks = True ):
466- """Whether this path is a directory."""
467- if not follow_symlinks and self .is_symlink ():
468- return False
469- try :
470- return self ._is_dir
471- except AttributeError :
472- if os .path .isdir (self ._path ):
473- self ._is_dir = self ._exists = True
474- return True
475- else :
476- self ._is_dir = False
449+ else :
450+ if self ._lstat_result is _STAT_RESULT_ERROR :
477451 return False
478-
479- def is_file (self , * , follow_symlinks = True ):
480- """Whether this path is a regular file."""
481- if not follow_symlinks and self .is_symlink ():
482- return False
483- try :
484- return self ._is_file
485- except AttributeError :
486- if os .path .isfile (self ._path ):
487- self ._is_file = self ._exists = True
452+ elif self ._dir_entry :
488453 return True
489- else :
490- self ._is_file = False
491- return False
492-
493- def is_symlink (self ):
494- """Whether this path is a symbolic link."""
495454 try :
496- return self ._is_symlink
497- except AttributeError :
498- self ._is_symlink = os .path .islink (self ._path )
499- return self ._is_symlink
500-
501-
502- class _PosixPathInfo (_PathInfoBase ):
503- """Implementation of pathlib.types.PathInfo that provides status
504- information for POSIX paths. Don't try to construct it yourself."""
505- __slots__ = ()
506-
507- def exists (self , * , follow_symlinks = True ):
508- """Whether this path exists."""
509- st = self ._stat (follow_symlinks = follow_symlinks , ignore_errors = True )
510- if st is None :
511- return False
512- return True
513-
514- def is_dir (self , * , follow_symlinks = True ):
515- """Whether this path is a directory."""
516- st = self ._stat (follow_symlinks = follow_symlinks , ignore_errors = True )
517- if st is None :
518- return False
519- return S_ISDIR (st .st_mode )
520-
521- def is_file (self , * , follow_symlinks = True ):
522- """Whether this path is a regular file."""
523- st = self ._stat (follow_symlinks = follow_symlinks , ignore_errors = True )
524- if st is None :
525- return False
526- return S_ISREG (st .st_mode )
527-
528- def is_symlink (self ):
529- """Whether this path is a symbolic link."""
530- st = self ._stat (follow_symlinks = False , ignore_errors = True )
531- if st is None :
455+ self ._stat (follow_symlinks = follow_symlinks )
456+ except (OSError , ValueError ):
532457 return False
533- return S_ISLNK (st .st_mode )
534-
535-
536- PathInfo = _WindowsPathInfo if os .name == 'nt' else _PosixPathInfo
537-
538-
539- class DirEntryInfo (_PathInfoBase ):
540- """Implementation of pathlib.types.PathInfo that provides status
541- information by querying a wrapped os.DirEntry object. Don't try to
542- construct it yourself."""
543- __slots__ = ('_entry' ,)
544-
545- def __init__ (self , entry ):
546- super ().__init__ (entry .path )
547- self ._entry = entry
548-
549- def _stat (self , * , follow_symlinks = True , ignore_errors = False ):
550- try :
551- return self ._entry .stat (follow_symlinks = follow_symlinks )
552- except OSError :
553- if not ignore_errors :
554- raise
555- return None
556-
557- def exists (self , * , follow_symlinks = True ):
558- """Whether this path exists."""
559- if not follow_symlinks :
458+ else :
560459 return True
561- return self ._stat (ignore_errors = True ) is not None
562460
563461 def is_dir (self , * , follow_symlinks = True ):
564462 """Whether this path is a directory."""
463+ if self ._dir_entry :
464+ try :
465+ return self ._dir_entry .is_dir (follow_symlinks = follow_symlinks )
466+ except OSError :
467+ return False
468+ elif follow_symlinks :
469+ if self ._stat_result is _STAT_RESULT_ERROR :
470+ return False
471+ else :
472+ if self ._lstat_result is _STAT_RESULT_ERROR :
473+ return False
565474 try :
566- return self ._entry . is_dir (follow_symlinks = follow_symlinks )
567- except OSError :
475+ st = self ._stat (follow_symlinks = follow_symlinks )
476+ except ( OSError , ValueError ) :
568477 return False
478+ else :
479+ return S_ISDIR (st .st_mode )
569480
570481 def is_file (self , * , follow_symlinks = True ):
571482 """Whether this path is a regular file."""
483+ if self ._dir_entry :
484+ try :
485+ return self ._dir_entry .is_file (follow_symlinks = follow_symlinks )
486+ except OSError :
487+ return False
488+ elif follow_symlinks :
489+ if self ._stat_result is _STAT_RESULT_ERROR :
490+ return False
491+ else :
492+ if self ._lstat_result is _STAT_RESULT_ERROR :
493+ return False
572494 try :
573- return self ._entry . is_file (follow_symlinks = follow_symlinks )
574- except OSError :
495+ st = self ._stat (follow_symlinks = follow_symlinks )
496+ except ( OSError , ValueError ) :
575497 return False
498+ else :
499+ return S_ISREG (st .st_mode )
576500
577501 def is_symlink (self ):
578502 """Whether this path is a symbolic link."""
503+ if self ._dir_entry :
504+ try :
505+ return self ._dir_entry .is_symlink ()
506+ except OSError :
507+ return False
508+ elif self ._lstat_result is _STAT_RESULT_ERROR :
509+ return False
579510 try :
580- return self ._entry . is_symlink ( )
581- except OSError :
511+ st = self ._stat ( follow_symlinks = False )
512+ except ( OSError , ValueError ) :
582513 return False
514+ else :
515+ return S_ISLNK (st .st_mode )
0 commit comments