Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
156 changes: 60 additions & 96 deletions Lib/pathlib/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand All @@ -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
Expand All @@ -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
Expand All @@ -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
Expand All @@ -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:
Expand All @@ -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):
Expand Down Expand Up @@ -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):
Expand Down Expand Up @@ -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):
Expand Down
Loading