Skip to content

Commit 1bfe86c

Browse files
authored
GH-139174: Prepare pathlib.Path.info for new methods (part 2) (#140155)
Merge `_Info`, `_StatResultInfo` and `_DirEntryInfo` into a single `_Info` class. No other changes. This will allow us to use a cached `os.stat()` result from our upcoming `_Info.stat()` method even when we have a backing `os.DirEntry`.
1 parent f4e51f2 commit 1bfe86c

File tree

1 file changed

+60
-96
lines changed

1 file changed

+60
-96
lines changed

Lib/pathlib/__init__.py

Lines changed: 60 additions & 96 deletions
Original file line numberDiff line numberDiff line change
@@ -611,72 +611,29 @@ class PureWindowsPath(PurePath):
611611
__slots__ = ()
612612

613613

614-
class _Info:
615-
__slots__ = ('_path',)
616-
617-
def __init__(self, path):
618-
self._path = path
619-
620-
def __repr__(self):
621-
path_type = "WindowsPath" if os.name == "nt" else "PosixPath"
622-
return f"<{path_type}.info>"
623-
624-
def _stat(self, *, follow_symlinks=True):
625-
"""Return the status as an os.stat_result."""
626-
raise NotImplementedError
627-
628-
def _posix_permissions(self, *, follow_symlinks=True):
629-
"""Return the POSIX file permissions."""
630-
return S_IMODE(self._stat(follow_symlinks=follow_symlinks).st_mode)
631-
632-
def _file_id(self, *, follow_symlinks=True):
633-
"""Returns the identifier of the file."""
634-
st = self._stat(follow_symlinks=follow_symlinks)
635-
return st.st_dev, st.st_ino
636-
637-
def _access_time_ns(self, *, follow_symlinks=True):
638-
"""Return the access time in nanoseconds."""
639-
return self._stat(follow_symlinks=follow_symlinks).st_atime_ns
640-
641-
def _mod_time_ns(self, *, follow_symlinks=True):
642-
"""Return the modify time in nanoseconds."""
643-
return self._stat(follow_symlinks=follow_symlinks).st_mtime_ns
644-
645-
if hasattr(os.stat_result, 'st_flags'):
646-
def _bsd_flags(self, *, follow_symlinks=True):
647-
"""Return the flags."""
648-
return self._stat(follow_symlinks=follow_symlinks).st_flags
649-
650-
if hasattr(os, 'listxattr'):
651-
def _xattrs(self, *, follow_symlinks=True):
652-
"""Return the xattrs as a list of (attr, value) pairs, or an empty
653-
list if extended attributes aren't supported."""
654-
try:
655-
return [
656-
(attr, os.getxattr(self._path, attr, follow_symlinks=follow_symlinks))
657-
for attr in os.listxattr(self._path, follow_symlinks=follow_symlinks)]
658-
except OSError as err:
659-
if err.errno not in (EPERM, ENOTSUP, ENODATA, EINVAL, EACCES):
660-
raise
661-
return []
662-
663-
664614
_STAT_RESULT_ERROR = [] # falsy sentinel indicating stat() failed.
665615

666616

667-
class _StatResultInfo(_Info):
617+
class _Info:
668618
"""Implementation of pathlib.types.PathInfo that provides status
669619
information by querying a wrapped os.stat_result object. Don't try to
670620
construct it yourself."""
671-
__slots__ = ('_stat_result', '_lstat_result')
621+
__slots__ = ('_path', '_entry', '_stat_result', '_lstat_result')
672622

673-
def __init__(self, path):
674-
super().__init__(path)
623+
def __init__(self, path, entry=None):
624+
self._path = path
625+
self._entry = entry
675626
self._stat_result = None
676627
self._lstat_result = None
677628

629+
def __repr__(self):
630+
path_type = "WindowsPath" if os.name == "nt" else "PosixPath"
631+
return f"<{path_type}.info>"
632+
678633
def _stat(self, *, follow_symlinks=True):
679634
"""Return the status as an os.stat_result."""
635+
if self._entry:
636+
return self._entry.stat(follow_symlinks=follow_symlinks)
680637
if follow_symlinks:
681638
if not self._stat_result:
682639
try:
@@ -696,6 +653,9 @@ def _stat(self, *, follow_symlinks=True):
696653

697654
def exists(self, *, follow_symlinks=True):
698655
"""Whether this path exists."""
656+
if self._entry:
657+
if not follow_symlinks:
658+
return True
699659
if follow_symlinks:
700660
if self._stat_result is _STAT_RESULT_ERROR:
701661
return False
@@ -710,6 +670,11 @@ def exists(self, *, follow_symlinks=True):
710670

711671
def is_dir(self, *, follow_symlinks=True):
712672
"""Whether this path is a directory."""
673+
if self._entry:
674+
try:
675+
return self._entry.is_dir(follow_symlinks=follow_symlinks)
676+
except OSError:
677+
return False
713678
if follow_symlinks:
714679
if self._stat_result is _STAT_RESULT_ERROR:
715680
return False
@@ -724,6 +689,11 @@ def is_dir(self, *, follow_symlinks=True):
724689

725690
def is_file(self, *, follow_symlinks=True):
726691
"""Whether this path is a regular file."""
692+
if self._entry:
693+
try:
694+
return self._entry.is_file(follow_symlinks=follow_symlinks)
695+
except OSError:
696+
return False
727697
if follow_symlinks:
728698
if self._stat_result is _STAT_RESULT_ERROR:
729699
return False
@@ -738,6 +708,11 @@ def is_file(self, *, follow_symlinks=True):
738708

739709
def is_symlink(self):
740710
"""Whether this path is a symbolic link."""
711+
if self._entry:
712+
try:
713+
return self._entry.is_symlink()
714+
except OSError:
715+
return False
741716
if self._lstat_result is _STAT_RESULT_ERROR:
742717
return False
743718
try:
@@ -746,51 +721,40 @@ def is_symlink(self):
746721
return False
747722
return S_ISLNK(st.st_mode)
748723

724+
def _posix_permissions(self, *, follow_symlinks=True):
725+
"""Return the POSIX file permissions."""
726+
return S_IMODE(self._stat(follow_symlinks=follow_symlinks).st_mode)
749727

750-
class _DirEntryInfo(_Info):
751-
"""Implementation of pathlib.types.PathInfo that provides status
752-
information by querying a wrapped os.DirEntry object. Don't try to
753-
construct it yourself."""
754-
__slots__ = ('_entry',)
755-
756-
def __init__(self, entry):
757-
super().__init__(entry.path)
758-
self._entry = entry
759-
760-
def _stat(self, *, follow_symlinks=True):
761-
"""Return the status as an os.stat_result."""
762-
return self._entry.stat(follow_symlinks=follow_symlinks)
728+
def _file_id(self, *, follow_symlinks=True):
729+
"""Returns the identifier of the file."""
730+
st = self._stat(follow_symlinks=follow_symlinks)
731+
return st.st_dev, st.st_ino
763732

764-
def exists(self, *, follow_symlinks=True):
765-
"""Whether this path exists."""
766-
if not follow_symlinks:
767-
return True
768-
try:
769-
self._stat(follow_symlinks=follow_symlinks)
770-
except OSError:
771-
return False
772-
return True
733+
def _access_time_ns(self, *, follow_symlinks=True):
734+
"""Return the access time in nanoseconds."""
735+
return self._stat(follow_symlinks=follow_symlinks).st_atime_ns
773736

774-
def is_dir(self, *, follow_symlinks=True):
775-
"""Whether this path is a directory."""
776-
try:
777-
return self._entry.is_dir(follow_symlinks=follow_symlinks)
778-
except OSError:
779-
return False
737+
def _mod_time_ns(self, *, follow_symlinks=True):
738+
"""Return the modify time in nanoseconds."""
739+
return self._stat(follow_symlinks=follow_symlinks).st_mtime_ns
780740

781-
def is_file(self, *, follow_symlinks=True):
782-
"""Whether this path is a regular file."""
783-
try:
784-
return self._entry.is_file(follow_symlinks=follow_symlinks)
785-
except OSError:
786-
return False
741+
if hasattr(os.stat_result, 'st_flags'):
742+
def _bsd_flags(self, *, follow_symlinks=True):
743+
"""Return the flags."""
744+
return self._stat(follow_symlinks=follow_symlinks).st_flags
787745

788-
def is_symlink(self):
789-
"""Whether this path is a symbolic link."""
790-
try:
791-
return self._entry.is_symlink()
792-
except OSError:
793-
return False
746+
if hasattr(os, 'listxattr'):
747+
def _xattrs(self, *, follow_symlinks=True):
748+
"""Return the xattrs as a list of (attr, value) pairs, or an empty
749+
list if extended attributes aren't supported."""
750+
try:
751+
return [
752+
(attr, os.getxattr(self._path, attr, follow_symlinks=follow_symlinks))
753+
for attr in os.listxattr(self._path, follow_symlinks=follow_symlinks)]
754+
except OSError as err:
755+
if err.errno not in (EPERM, ENOTSUP, ENODATA, EINVAL, EACCES):
756+
raise
757+
return []
794758

795759

796760
def _copy_info(info, target, follow_symlinks=True):
@@ -877,7 +841,7 @@ def info(self):
877841
try:
878842
return self._info
879843
except AttributeError:
880-
self._info = _StatResultInfo(str(self))
844+
self._info = _Info(str(self))
881845
return self._info
882846

883847
def stat(self, *, follow_symlinks=True):
@@ -1057,7 +1021,7 @@ def _filter_trailing_slash(self, paths):
10571021
def _from_dir_entry(self, dir_entry, path_str):
10581022
path = self.with_segments(path_str)
10591023
path._str = path_str
1060-
path._info = _DirEntryInfo(dir_entry)
1024+
path._info = _Info(dir_entry.path, dir_entry)
10611025
return path
10621026

10631027
def iterdir(self):

0 commit comments

Comments
 (0)