https://github.com/python/cpython/commit/1bfe86caaaaca3ec16351d69fa778f1212f1dd84
commit: 1bfe86caaaaca3ec16351d69fa778f1212f1dd84
branch: main
author: Barney Gale <[email protected]>
committer: barneygale <[email protected]>
date: 2025-10-18T02:13:25+01:00
summary:
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`.
files:
M Lib/pathlib/__init__.py
diff --git a/Lib/pathlib/__init__.py b/Lib/pathlib/__init__.py
index 51359cec8b0f9e..44f967eb12dd4f 100644
--- a/Lib/pathlib/__init__.py
+++ b/Lib/pathlib/__init__.py
@@ -611,72 +611,29 @@ class PureWindowsPath(PurePath):
__slots__ = ()
-class _Info:
- __slots__ = ('_path',)
-
- def __init__(self, path):
- self._path = path
-
- def __repr__(self):
- path_type = "WindowsPath" if os.name == "nt" else "PosixPath"
- return f"<{path_type}.info>"
-
- def _stat(self, *, follow_symlinks=True):
- """Return the status as an os.stat_result."""
- raise NotImplementedError
-
- def _posix_permissions(self, *, follow_symlinks=True):
- """Return the POSIX file permissions."""
- return S_IMODE(self._stat(follow_symlinks=follow_symlinks).st_mode)
-
- def _file_id(self, *, follow_symlinks=True):
- """Returns the identifier of the file."""
- st = self._stat(follow_symlinks=follow_symlinks)
- return st.st_dev, st.st_ino
-
- def _access_time_ns(self, *, follow_symlinks=True):
- """Return the access time in nanoseconds."""
- return self._stat(follow_symlinks=follow_symlinks).st_atime_ns
-
- def _mod_time_ns(self, *, follow_symlinks=True):
- """Return the modify time in nanoseconds."""
- return self._stat(follow_symlinks=follow_symlinks).st_mtime_ns
-
- if hasattr(os.stat_result, 'st_flags'):
- def _bsd_flags(self, *, follow_symlinks=True):
- """Return the flags."""
- return self._stat(follow_symlinks=follow_symlinks).st_flags
-
- if hasattr(os, 'listxattr'):
- def _xattrs(self, *, follow_symlinks=True):
- """Return the xattrs as a list of (attr, value) pairs, or an empty
- list if extended attributes aren't supported."""
- try:
- return [
- (attr, os.getxattr(self._path, attr,
follow_symlinks=follow_symlinks))
- for attr in os.listxattr(self._path,
follow_symlinks=follow_symlinks)]
- except OSError as err:
- if err.errno not in (EPERM, ENOTSUP, ENODATA, EINVAL, EACCES):
- raise
- return []
-
-
_STAT_RESULT_ERROR = [] # falsy sentinel indicating stat() failed.
-class _StatResultInfo(_Info):
+class _Info:
"""Implementation of pathlib.types.PathInfo that provides status
information by querying a wrapped os.stat_result object. Don't try to
construct it yourself."""
- __slots__ = ('_stat_result', '_lstat_result')
+ __slots__ = ('_path', '_entry', '_stat_result', '_lstat_result')
- def __init__(self, path):
- super().__init__(path)
+ def __init__(self, path, entry=None):
+ self._path = path
+ self._entry = entry
self._stat_result = None
self._lstat_result = None
+ def __repr__(self):
+ path_type = "WindowsPath" if os.name == "nt" else "PosixPath"
+ return f"<{path_type}.info>"
+
def _stat(self, *, follow_symlinks=True):
"""Return the status as an os.stat_result."""
+ if self._entry:
+ return self._entry.stat(follow_symlinks=follow_symlinks)
if follow_symlinks:
if not self._stat_result:
try:
@@ -696,6 +653,9 @@ def _stat(self, *, follow_symlinks=True):
def exists(self, *, follow_symlinks=True):
"""Whether this path exists."""
+ if self._entry:
+ if not follow_symlinks:
+ return True
if follow_symlinks:
if self._stat_result is _STAT_RESULT_ERROR:
return False
@@ -710,6 +670,11 @@ def exists(self, *, follow_symlinks=True):
def is_dir(self, *, follow_symlinks=True):
"""Whether this path is a directory."""
+ if self._entry:
+ try:
+ return self._entry.is_dir(follow_symlinks=follow_symlinks)
+ except OSError:
+ return False
if follow_symlinks:
if self._stat_result is _STAT_RESULT_ERROR:
return False
@@ -724,6 +689,11 @@ def is_dir(self, *, follow_symlinks=True):
def is_file(self, *, follow_symlinks=True):
"""Whether this path is a regular file."""
+ if self._entry:
+ try:
+ return self._entry.is_file(follow_symlinks=follow_symlinks)
+ except OSError:
+ return False
if follow_symlinks:
if self._stat_result is _STAT_RESULT_ERROR:
return False
@@ -738,6 +708,11 @@ def is_file(self, *, follow_symlinks=True):
def is_symlink(self):
"""Whether this path is a symbolic link."""
+ if self._entry:
+ try:
+ return self._entry.is_symlink()
+ except OSError:
+ return False
if self._lstat_result is _STAT_RESULT_ERROR:
return False
try:
@@ -746,51 +721,40 @@ def is_symlink(self):
return False
return S_ISLNK(st.st_mode)
+ def _posix_permissions(self, *, follow_symlinks=True):
+ """Return the POSIX file permissions."""
+ return S_IMODE(self._stat(follow_symlinks=follow_symlinks).st_mode)
-class _DirEntryInfo(_Info):
- """Implementation of pathlib.types.PathInfo that provides status
- information by querying a wrapped os.DirEntry object. Don't try to
- construct it yourself."""
- __slots__ = ('_entry',)
-
- def __init__(self, entry):
- super().__init__(entry.path)
- self._entry = entry
-
- def _stat(self, *, follow_symlinks=True):
- """Return the status as an os.stat_result."""
- return self._entry.stat(follow_symlinks=follow_symlinks)
+ def _file_id(self, *, follow_symlinks=True):
+ """Returns the identifier of the file."""
+ st = self._stat(follow_symlinks=follow_symlinks)
+ return st.st_dev, st.st_ino
- def exists(self, *, follow_symlinks=True):
- """Whether this path exists."""
- if not follow_symlinks:
- return True
- try:
- self._stat(follow_symlinks=follow_symlinks)
- except OSError:
- return False
- return True
+ def _access_time_ns(self, *, follow_symlinks=True):
+ """Return the access time in nanoseconds."""
+ return self._stat(follow_symlinks=follow_symlinks).st_atime_ns
- def is_dir(self, *, follow_symlinks=True):
- """Whether this path is a directory."""
- try:
- return self._entry.is_dir(follow_symlinks=follow_symlinks)
- except OSError:
- return False
+ def _mod_time_ns(self, *, follow_symlinks=True):
+ """Return the modify time in nanoseconds."""
+ return self._stat(follow_symlinks=follow_symlinks).st_mtime_ns
- def is_file(self, *, follow_symlinks=True):
- """Whether this path is a regular file."""
- try:
- return self._entry.is_file(follow_symlinks=follow_symlinks)
- except OSError:
- return False
+ if hasattr(os.stat_result, 'st_flags'):
+ def _bsd_flags(self, *, follow_symlinks=True):
+ """Return the flags."""
+ return self._stat(follow_symlinks=follow_symlinks).st_flags
- def is_symlink(self):
- """Whether this path is a symbolic link."""
- try:
- return self._entry.is_symlink()
- except OSError:
- return False
+ if hasattr(os, 'listxattr'):
+ def _xattrs(self, *, follow_symlinks=True):
+ """Return the xattrs as a list of (attr, value) pairs, or an empty
+ list if extended attributes aren't supported."""
+ try:
+ return [
+ (attr, os.getxattr(self._path, attr,
follow_symlinks=follow_symlinks))
+ for attr in os.listxattr(self._path,
follow_symlinks=follow_symlinks)]
+ except OSError as err:
+ if err.errno not in (EPERM, ENOTSUP, ENODATA, EINVAL, EACCES):
+ raise
+ return []
def _copy_info(info, target, follow_symlinks=True):
@@ -877,7 +841,7 @@ def info(self):
try:
return self._info
except AttributeError:
- self._info = _StatResultInfo(str(self))
+ self._info = _Info(str(self))
return self._info
def stat(self, *, follow_symlinks=True):
@@ -1057,7 +1021,7 @@ def _filter_trailing_slash(self, paths):
def _from_dir_entry(self, dir_entry, path_str):
path = self.with_segments(path_str)
path._str = path_str
- path._info = _DirEntryInfo(dir_entry)
+ path._info = _Info(dir_entry.path, dir_entry)
return path
def iterdir(self):
_______________________________________________
Python-checkins mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3//lists/python-checkins.python.org
Member address: [email protected]