mirror of
https://github.com/python/cpython.git
synced 2025-08-03 08:34:29 +00:00
GH-125413: Add pathlib.Path.info
attribute (#127730)
Add `pathlib.Path.info` attribute, which stores an object implementing the `pathlib.types.PathInfo` protocol (also new). The object supports querying the file type and internally caching `os.stat()` results. Path objects generated by `Path.iterdir()` are initialised with status information from `os.DirEntry` objects, which is gleaned from scanning the parent directory. The `PathInfo` protocol has four methods: `exists()`, `is_dir()`, `is_file()` and `is_symlink()`.
This commit is contained in:
parent
a1417b211f
commit
718ab66299
10 changed files with 526 additions and 101 deletions
|
@ -3,6 +3,7 @@ Low-level OS functionality wrappers used by pathlib.
|
|||
"""
|
||||
|
||||
from errno import *
|
||||
from stat import S_ISDIR, S_ISREG, S_ISLNK
|
||||
import os
|
||||
import sys
|
||||
try:
|
||||
|
@ -162,3 +163,162 @@ def copyfileobj(source_f, target_f):
|
|||
write_target = target_f.write
|
||||
while buf := read_source(1024 * 1024):
|
||||
write_target(buf)
|
||||
|
||||
|
||||
class _PathInfoBase:
|
||||
__slots__ = ()
|
||||
|
||||
def __repr__(self):
|
||||
path_type = "WindowsPath" if os.name == "nt" else "PosixPath"
|
||||
return f"<{path_type}.info>"
|
||||
|
||||
|
||||
class _WindowsPathInfo(_PathInfoBase):
|
||||
"""Implementation of pathlib.types.PathInfo that provides status
|
||||
information for Windows paths. Don't try to construct it yourself."""
|
||||
__slots__ = ('_path', '_exists', '_is_dir', '_is_file', '_is_symlink')
|
||||
|
||||
def __init__(self, path):
|
||||
self._path = str(path)
|
||||
|
||||
def exists(self, *, follow_symlinks=True):
|
||||
"""Whether this path exists."""
|
||||
if not follow_symlinks and self.is_symlink():
|
||||
return True
|
||||
try:
|
||||
return self._exists
|
||||
except AttributeError:
|
||||
if os.path.exists(self._path):
|
||||
self._exists = True
|
||||
return True
|
||||
else:
|
||||
self._exists = self._is_dir = self._is_file = False
|
||||
return False
|
||||
|
||||
def is_dir(self, *, follow_symlinks=True):
|
||||
"""Whether this path is a directory."""
|
||||
if not follow_symlinks and self.is_symlink():
|
||||
return False
|
||||
try:
|
||||
return self._is_dir
|
||||
except AttributeError:
|
||||
if os.path.isdir(self._path):
|
||||
self._is_dir = self._exists = True
|
||||
return True
|
||||
else:
|
||||
self._is_dir = False
|
||||
return False
|
||||
|
||||
def is_file(self, *, follow_symlinks=True):
|
||||
"""Whether this path is a regular file."""
|
||||
if not follow_symlinks and self.is_symlink():
|
||||
return False
|
||||
try:
|
||||
return self._is_file
|
||||
except AttributeError:
|
||||
if os.path.isfile(self._path):
|
||||
self._is_file = self._exists = True
|
||||
return True
|
||||
else:
|
||||
self._is_file = False
|
||||
return False
|
||||
|
||||
def is_symlink(self):
|
||||
"""Whether this path is a symbolic link."""
|
||||
try:
|
||||
return self._is_symlink
|
||||
except AttributeError:
|
||||
self._is_symlink = os.path.islink(self._path)
|
||||
return self._is_symlink
|
||||
|
||||
|
||||
class _PosixPathInfo(_PathInfoBase):
|
||||
"""Implementation of pathlib.types.PathInfo that provides status
|
||||
information for POSIX paths. Don't try to construct it yourself."""
|
||||
__slots__ = ('_path', '_mode')
|
||||
|
||||
def __init__(self, path):
|
||||
self._path = str(path)
|
||||
self._mode = [None, None]
|
||||
|
||||
def _get_mode(self, *, follow_symlinks=True):
|
||||
idx = bool(follow_symlinks)
|
||||
mode = self._mode[idx]
|
||||
if mode is None:
|
||||
try:
|
||||
st = os.stat(self._path, follow_symlinks=follow_symlinks)
|
||||
except (OSError, ValueError):
|
||||
mode = 0
|
||||
else:
|
||||
mode = st.st_mode
|
||||
if follow_symlinks or S_ISLNK(mode):
|
||||
self._mode[idx] = mode
|
||||
else:
|
||||
# Not a symlink, so stat() will give the same result
|
||||
self._mode = [mode, mode]
|
||||
return mode
|
||||
|
||||
def exists(self, *, follow_symlinks=True):
|
||||
"""Whether this path exists."""
|
||||
return self._get_mode(follow_symlinks=follow_symlinks) > 0
|
||||
|
||||
def is_dir(self, *, follow_symlinks=True):
|
||||
"""Whether this path is a directory."""
|
||||
return S_ISDIR(self._get_mode(follow_symlinks=follow_symlinks))
|
||||
|
||||
def is_file(self, *, follow_symlinks=True):
|
||||
"""Whether this path is a regular file."""
|
||||
return S_ISREG(self._get_mode(follow_symlinks=follow_symlinks))
|
||||
|
||||
def is_symlink(self):
|
||||
"""Whether this path is a symbolic link."""
|
||||
return S_ISLNK(self._get_mode(follow_symlinks=False))
|
||||
|
||||
|
||||
PathInfo = _WindowsPathInfo if os.name == 'nt' else _PosixPathInfo
|
||||
|
||||
|
||||
class DirEntryInfo(_PathInfoBase):
|
||||
"""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', '_exists')
|
||||
|
||||
def __init__(self, entry):
|
||||
self._entry = entry
|
||||
|
||||
def exists(self, *, follow_symlinks=True):
|
||||
"""Whether this path exists."""
|
||||
if not follow_symlinks:
|
||||
return True
|
||||
try:
|
||||
return self._exists
|
||||
except AttributeError:
|
||||
try:
|
||||
self._entry.stat()
|
||||
except OSError:
|
||||
self._exists = False
|
||||
else:
|
||||
self._exists = True
|
||||
return self._exists
|
||||
|
||||
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 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
|
||||
|
||||
def is_symlink(self):
|
||||
"""Whether this path is a symbolic link."""
|
||||
try:
|
||||
return self._entry.is_symlink()
|
||||
except OSError:
|
||||
return False
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue