@@ -363,49 +363,19 @@ def copy_info(info, target, follow_symlinks=True):
363363 raise
364364
365365
366- class _PathInfoBase :
367- __slots__ = ('_path' , '_stat_result' , '_lstat_result' )
366+ class _LocalPathInfo :
367+ __slots__ = ('_path' ,)
368368
369369 def __init__ (self , path ):
370- self ._path = str ( path )
370+ self ._path = path
371371
372372 def __repr__ (self ):
373373 path_type = "WindowsPath" if os .name == "nt" else "PosixPath"
374374 return f"<{ path_type } .info>"
375375
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."""
379- 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 :
392- raise
393- return self ._stat_result
394- 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 :
407- raise
408- return self ._lstat_result
376+ def _stat (self , * , follow_symlinks = True ):
377+ """Return the status as an os.stat_result."""
378+ raise NotImplementedError
409379
410380 def _posix_permissions (self , * , follow_symlinks = True ):
411381 """Return the POSIX file permissions."""
@@ -443,100 +413,93 @@ def _xattrs(self, *, follow_symlinks=True):
443413 return []
444414
445415
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' )
416+ _STAT_RESULT_ERROR = [] # falsy sentinel indicating stat() failed.
450417
451- def exists (self , * , follow_symlinks = True ):
452- """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
463- return False
464418
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
477- 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
488- 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."""
495- try :
496- return self ._is_symlink
497- except AttributeError :
498- self ._is_symlink = os .path .islink (self ._path )
499- return self ._is_symlink
419+ class StatResultInfo (_LocalPathInfo ):
420+ """Implementation of pathlib.types.PathInfo that provides status
421+ information by querying a wrapped os.stat_result object. Don't try to
422+ construct it yourself."""
423+ __slots__ = ('_stat_result' , '_lstat_result' )
500424
425+ def __init__ (self , path ):
426+ super ().__init__ (path )
427+ self ._stat_result = None
428+ self ._lstat_result = None
501429
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__ = ()
430+ def _stat (self , * , follow_symlinks = True ):
431+ """Return the status as an os.stat_result."""
432+ if follow_symlinks :
433+ if not self ._stat_result :
434+ try :
435+ self ._stat_result = os .stat (self ._path )
436+ except (OSError , ValueError ):
437+ self ._stat_result = _STAT_RESULT_ERROR
438+ raise
439+ return self ._stat_result
440+ else :
441+ if not self ._lstat_result :
442+ try :
443+ self ._lstat_result = os .lstat (self ._path )
444+ except (OSError , ValueError ):
445+ self ._lstat_result = _STAT_RESULT_ERROR
446+ raise
447+ return self ._lstat_result
506448
507449 def exists (self , * , follow_symlinks = True ):
508450 """Whether this path exists."""
509- st = self ._stat (follow_symlinks = follow_symlinks , ignore_errors = True )
510- if st is None :
451+ if follow_symlinks :
452+ if self ._stat_result is _STAT_RESULT_ERROR :
453+ return False
454+ else :
455+ if self ._lstat_result is _STAT_RESULT_ERROR :
456+ return False
457+ try :
458+ self ._stat (follow_symlinks = follow_symlinks )
459+ except (OSError , ValueError ):
511460 return False
512461 return True
513462
514463 def is_dir (self , * , follow_symlinks = True ):
515464 """Whether this path is a directory."""
516- st = self ._stat (follow_symlinks = follow_symlinks , ignore_errors = True )
517- if st is None :
465+ if follow_symlinks :
466+ if self ._stat_result is _STAT_RESULT_ERROR :
467+ return False
468+ else :
469+ if self ._lstat_result is _STAT_RESULT_ERROR :
470+ return False
471+ try :
472+ st = self ._stat (follow_symlinks = follow_symlinks )
473+ except (OSError , ValueError ):
518474 return False
519475 return S_ISDIR (st .st_mode )
520476
521477 def is_file (self , * , follow_symlinks = True ):
522478 """Whether this path is a regular file."""
523- st = self ._stat (follow_symlinks = follow_symlinks , ignore_errors = True )
524- if st is None :
479+ if follow_symlinks :
480+ if self ._stat_result is _STAT_RESULT_ERROR :
481+ return False
482+ else :
483+ if self ._lstat_result is _STAT_RESULT_ERROR :
484+ return False
485+ try :
486+ st = self ._stat (follow_symlinks = follow_symlinks )
487+ except (OSError , ValueError ):
525488 return False
526489 return S_ISREG (st .st_mode )
527490
528491 def is_symlink (self ):
529492 """Whether this path is a symbolic link."""
530- st = self ._stat (follow_symlinks = False , ignore_errors = True )
531- if st is None :
493+ if self ._lstat_result is _STAT_RESULT_ERROR :
494+ return False
495+ try :
496+ st = self ._stat (follow_symlinks = False )
497+ except (OSError , ValueError ):
532498 return False
533499 return S_ISLNK (st .st_mode )
534500
535501
536- PathInfo = _WindowsPathInfo if os .name == 'nt' else _PosixPathInfo
537-
538-
539- class DirEntryInfo (_PathInfoBase ):
502+ class DirEntryInfo (_LocalPathInfo ):
540503 """Implementation of pathlib.types.PathInfo that provides status
541504 information by querying a wrapped os.DirEntry object. Don't try to
542505 construct it yourself."""
@@ -546,19 +509,19 @@ def __init__(self, entry):
546509 super ().__init__ (entry .path )
547510 self ._entry = entry
548511
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
512+ def _stat (self , * , follow_symlinks = True ):
513+ """Return the status as an os.stat_result."""
514+ return self ._entry .stat (follow_symlinks = follow_symlinks )
556515
557516 def exists (self , * , follow_symlinks = True ):
558517 """Whether this path exists."""
559518 if not follow_symlinks :
560519 return True
561- return self ._stat (ignore_errors = True ) is not None
520+ try :
521+ self ._stat (follow_symlinks = follow_symlinks )
522+ except (OSError , ValueError ):
523+ return False
524+ return True
562525
563526 def is_dir (self , * , follow_symlinks = True ):
564527 """Whether this path is a directory."""
0 commit comments