mirror of
https://github.com/python/cpython.git
synced 2025-09-26 18:29:57 +00:00
GH-127381: pathlib ABCs: remove PathBase.stat()
(#128334)
Remove the `PathBase.stat()` method. Its use of the `os.stat_result` API, with its 10 mandatory fields and low-level types, makes it an awkward fit for virtual filesystems. We'll look to add a `PathBase.info` attribute later - see GH-125413.
This commit is contained in:
parent
7e819ce0f3
commit
c78729f2df
4 changed files with 62 additions and 83 deletions
|
@ -16,7 +16,6 @@ import operator
|
||||||
import posixpath
|
import posixpath
|
||||||
from errno import EINVAL
|
from errno import EINVAL
|
||||||
from glob import _GlobberBase, _no_recurse_symlinks
|
from glob import _GlobberBase, _no_recurse_symlinks
|
||||||
from stat import S_ISDIR, S_ISLNK, S_ISREG
|
|
||||||
from pathlib._os import copyfileobj
|
from pathlib._os import copyfileobj
|
||||||
|
|
||||||
|
|
||||||
|
@ -450,15 +449,6 @@ class PathBase(PurePathBase):
|
||||||
"""
|
"""
|
||||||
__slots__ = ()
|
__slots__ = ()
|
||||||
|
|
||||||
def stat(self, *, follow_symlinks=True):
|
|
||||||
"""
|
|
||||||
Return the result of the stat() system call on this path, like
|
|
||||||
os.stat() does.
|
|
||||||
"""
|
|
||||||
raise NotImplementedError
|
|
||||||
|
|
||||||
# Convenience functions for querying the stat results
|
|
||||||
|
|
||||||
def exists(self, *, follow_symlinks=True):
|
def exists(self, *, follow_symlinks=True):
|
||||||
"""
|
"""
|
||||||
Whether this path exists.
|
Whether this path exists.
|
||||||
|
@ -466,39 +456,26 @@ class PathBase(PurePathBase):
|
||||||
This method normally follows symlinks; to check whether a symlink exists,
|
This method normally follows symlinks; to check whether a symlink exists,
|
||||||
add the argument follow_symlinks=False.
|
add the argument follow_symlinks=False.
|
||||||
"""
|
"""
|
||||||
try:
|
raise NotImplementedError
|
||||||
self.stat(follow_symlinks=follow_symlinks)
|
|
||||||
except (OSError, ValueError):
|
|
||||||
return False
|
|
||||||
return True
|
|
||||||
|
|
||||||
def is_dir(self, *, follow_symlinks=True):
|
def is_dir(self, *, follow_symlinks=True):
|
||||||
"""
|
"""
|
||||||
Whether this path is a directory.
|
Whether this path is a directory.
|
||||||
"""
|
"""
|
||||||
try:
|
raise NotImplementedError
|
||||||
return S_ISDIR(self.stat(follow_symlinks=follow_symlinks).st_mode)
|
|
||||||
except (OSError, ValueError):
|
|
||||||
return False
|
|
||||||
|
|
||||||
def is_file(self, *, follow_symlinks=True):
|
def is_file(self, *, follow_symlinks=True):
|
||||||
"""
|
"""
|
||||||
Whether this path is a regular file (also True for symlinks pointing
|
Whether this path is a regular file (also True for symlinks pointing
|
||||||
to regular files).
|
to regular files).
|
||||||
"""
|
"""
|
||||||
try:
|
raise NotImplementedError
|
||||||
return S_ISREG(self.stat(follow_symlinks=follow_symlinks).st_mode)
|
|
||||||
except (OSError, ValueError):
|
|
||||||
return False
|
|
||||||
|
|
||||||
def is_symlink(self):
|
def is_symlink(self):
|
||||||
"""
|
"""
|
||||||
Whether this path is a symbolic link.
|
Whether this path is a symbolic link.
|
||||||
"""
|
"""
|
||||||
try:
|
raise NotImplementedError
|
||||||
return S_ISLNK(self.stat(follow_symlinks=False).st_mode)
|
|
||||||
except (OSError, ValueError):
|
|
||||||
return False
|
|
||||||
|
|
||||||
def open(self, mode='r', buffering=-1, encoding=None,
|
def open(self, mode='r', buffering=-1, encoding=None,
|
||||||
errors=None, newline=None):
|
errors=None, newline=None):
|
||||||
|
|
|
@ -7,7 +7,7 @@ import sys
|
||||||
from errno import *
|
from errno import *
|
||||||
from glob import _StringGlobber, _no_recurse_symlinks
|
from glob import _StringGlobber, _no_recurse_symlinks
|
||||||
from itertools import chain
|
from itertools import chain
|
||||||
from stat import S_IMODE, S_ISSOCK, S_ISBLK, S_ISCHR, S_ISFIFO
|
from stat import S_IMODE, S_ISDIR, S_ISREG, S_ISSOCK, S_ISBLK, S_ISCHR, S_ISFIFO
|
||||||
from _collections_abc import Sequence
|
from _collections_abc import Sequence
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
@ -725,7 +725,10 @@ class Path(PathBase, PurePath):
|
||||||
"""
|
"""
|
||||||
if follow_symlinks:
|
if follow_symlinks:
|
||||||
return os.path.isdir(self)
|
return os.path.isdir(self)
|
||||||
return PathBase.is_dir(self, follow_symlinks=follow_symlinks)
|
try:
|
||||||
|
return S_ISDIR(self.stat(follow_symlinks=follow_symlinks).st_mode)
|
||||||
|
except (OSError, ValueError):
|
||||||
|
return False
|
||||||
|
|
||||||
def is_file(self, *, follow_symlinks=True):
|
def is_file(self, *, follow_symlinks=True):
|
||||||
"""
|
"""
|
||||||
|
@ -734,7 +737,10 @@ class Path(PathBase, PurePath):
|
||||||
"""
|
"""
|
||||||
if follow_symlinks:
|
if follow_symlinks:
|
||||||
return os.path.isfile(self)
|
return os.path.isfile(self)
|
||||||
return PathBase.is_file(self, follow_symlinks=follow_symlinks)
|
try:
|
||||||
|
return S_ISREG(self.stat(follow_symlinks=follow_symlinks).st_mode)
|
||||||
|
except (OSError, ValueError):
|
||||||
|
return False
|
||||||
|
|
||||||
def is_mount(self):
|
def is_mount(self):
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -1835,6 +1835,31 @@ class PathTest(test_pathlib_abc.DummyPathTest, PurePathTest):
|
||||||
with self.assertRaises(pathlib.UnsupportedOperation):
|
with self.assertRaises(pathlib.UnsupportedOperation):
|
||||||
q.symlink_to(p)
|
q.symlink_to(p)
|
||||||
|
|
||||||
|
def test_stat(self):
|
||||||
|
statA = self.cls(self.base).joinpath('fileA').stat()
|
||||||
|
statB = self.cls(self.base).joinpath('dirB', 'fileB').stat()
|
||||||
|
statC = self.cls(self.base).joinpath('dirC').stat()
|
||||||
|
# st_mode: files are the same, directory differs.
|
||||||
|
self.assertIsInstance(statA.st_mode, int)
|
||||||
|
self.assertEqual(statA.st_mode, statB.st_mode)
|
||||||
|
self.assertNotEqual(statA.st_mode, statC.st_mode)
|
||||||
|
self.assertNotEqual(statB.st_mode, statC.st_mode)
|
||||||
|
# st_ino: all different,
|
||||||
|
self.assertIsInstance(statA.st_ino, int)
|
||||||
|
self.assertNotEqual(statA.st_ino, statB.st_ino)
|
||||||
|
self.assertNotEqual(statA.st_ino, statC.st_ino)
|
||||||
|
self.assertNotEqual(statB.st_ino, statC.st_ino)
|
||||||
|
# st_dev: all the same.
|
||||||
|
self.assertIsInstance(statA.st_dev, int)
|
||||||
|
self.assertEqual(statA.st_dev, statB.st_dev)
|
||||||
|
self.assertEqual(statA.st_dev, statC.st_dev)
|
||||||
|
# other attributes not used by pathlib.
|
||||||
|
|
||||||
|
def test_stat_no_follow_symlinks_nosymlink(self):
|
||||||
|
p = self.cls(self.base) / 'fileA'
|
||||||
|
st = p.stat()
|
||||||
|
self.assertEqual(st, p.stat(follow_symlinks=False))
|
||||||
|
|
||||||
@needs_symlinks
|
@needs_symlinks
|
||||||
def test_stat_no_follow_symlinks(self):
|
def test_stat_no_follow_symlinks(self):
|
||||||
p = self.cls(self.base) / 'linkA'
|
p = self.cls(self.base) / 'linkA'
|
||||||
|
|
|
@ -2,7 +2,6 @@ import collections
|
||||||
import io
|
import io
|
||||||
import os
|
import os
|
||||||
import errno
|
import errno
|
||||||
import stat
|
|
||||||
import unittest
|
import unittest
|
||||||
|
|
||||||
from pathlib._abc import PurePathBase, PathBase
|
from pathlib._abc import PurePathBase, PathBase
|
||||||
|
@ -1294,11 +1293,6 @@ class DummyPathIO(io.BytesIO):
|
||||||
super().close()
|
super().close()
|
||||||
|
|
||||||
|
|
||||||
DummyPathStatResult = collections.namedtuple(
|
|
||||||
'DummyPathStatResult',
|
|
||||||
'st_mode st_ino st_dev st_nlink st_uid st_gid st_size st_atime st_mtime st_ctime')
|
|
||||||
|
|
||||||
|
|
||||||
class DummyPath(PathBase):
|
class DummyPath(PathBase):
|
||||||
"""
|
"""
|
||||||
Simple implementation of PathBase that keeps files and directories in
|
Simple implementation of PathBase that keeps files and directories in
|
||||||
|
@ -1331,15 +1325,17 @@ class DummyPath(PathBase):
|
||||||
def with_segments(self, *pathsegments):
|
def with_segments(self, *pathsegments):
|
||||||
return type(self)(*pathsegments)
|
return type(self)(*pathsegments)
|
||||||
|
|
||||||
def stat(self, *, follow_symlinks=True):
|
def exists(self, *, follow_symlinks=True):
|
||||||
path = str(self).rstrip('/')
|
return self.is_dir() or self.is_file()
|
||||||
if path in self._files:
|
|
||||||
st_mode = stat.S_IFREG
|
def is_dir(self, *, follow_symlinks=True):
|
||||||
elif path in self._directories:
|
return str(self).rstrip('/') in self._directories
|
||||||
st_mode = stat.S_IFDIR
|
|
||||||
else:
|
def is_file(self, *, follow_symlinks=True):
|
||||||
raise FileNotFoundError(errno.ENOENT, "Not found", str(self))
|
return str(self) in self._files
|
||||||
return DummyPathStatResult(st_mode, hash(str(self)), 0, 0, 0, 0, 0, 0, 0, 0)
|
|
||||||
|
def is_symlink(self):
|
||||||
|
return False
|
||||||
|
|
||||||
def open(self, mode='r', buffering=-1, encoding=None,
|
def open(self, mode='r', buffering=-1, encoding=None,
|
||||||
errors=None, newline=None):
|
errors=None, newline=None):
|
||||||
|
@ -1958,31 +1954,6 @@ class DummyPathTest(DummyPurePathTest):
|
||||||
self.assertEqual(set(p.rglob("FILEd")), { P(self.base, "dirC/dirD/fileD") })
|
self.assertEqual(set(p.rglob("FILEd")), { P(self.base, "dirC/dirD/fileD") })
|
||||||
self.assertEqual(set(p.rglob("*\\")), { P(self.base, "dirC/dirD/") })
|
self.assertEqual(set(p.rglob("*\\")), { P(self.base, "dirC/dirD/") })
|
||||||
|
|
||||||
def test_stat(self):
|
|
||||||
statA = self.cls(self.base).joinpath('fileA').stat()
|
|
||||||
statB = self.cls(self.base).joinpath('dirB', 'fileB').stat()
|
|
||||||
statC = self.cls(self.base).joinpath('dirC').stat()
|
|
||||||
# st_mode: files are the same, directory differs.
|
|
||||||
self.assertIsInstance(statA.st_mode, int)
|
|
||||||
self.assertEqual(statA.st_mode, statB.st_mode)
|
|
||||||
self.assertNotEqual(statA.st_mode, statC.st_mode)
|
|
||||||
self.assertNotEqual(statB.st_mode, statC.st_mode)
|
|
||||||
# st_ino: all different,
|
|
||||||
self.assertIsInstance(statA.st_ino, int)
|
|
||||||
self.assertNotEqual(statA.st_ino, statB.st_ino)
|
|
||||||
self.assertNotEqual(statA.st_ino, statC.st_ino)
|
|
||||||
self.assertNotEqual(statB.st_ino, statC.st_ino)
|
|
||||||
# st_dev: all the same.
|
|
||||||
self.assertIsInstance(statA.st_dev, int)
|
|
||||||
self.assertEqual(statA.st_dev, statB.st_dev)
|
|
||||||
self.assertEqual(statA.st_dev, statC.st_dev)
|
|
||||||
# other attributes not used by pathlib.
|
|
||||||
|
|
||||||
def test_stat_no_follow_symlinks_nosymlink(self):
|
|
||||||
p = self.cls(self.base) / 'fileA'
|
|
||||||
st = p.stat()
|
|
||||||
self.assertEqual(st, p.stat(follow_symlinks=False))
|
|
||||||
|
|
||||||
def test_is_dir(self):
|
def test_is_dir(self):
|
||||||
P = self.cls(self.base)
|
P = self.cls(self.base)
|
||||||
self.assertTrue((P / 'dirA').is_dir())
|
self.assertTrue((P / 'dirA').is_dir())
|
||||||
|
@ -2054,26 +2025,26 @@ class DummyPathTest(DummyPurePathTest):
|
||||||
def test_delete_file(self):
|
def test_delete_file(self):
|
||||||
p = self.cls(self.base) / 'fileA'
|
p = self.cls(self.base) / 'fileA'
|
||||||
p._delete()
|
p._delete()
|
||||||
self.assertFileNotFound(p.stat)
|
self.assertFalse(p.exists())
|
||||||
self.assertFileNotFound(p._delete)
|
self.assertFileNotFound(p._delete)
|
||||||
|
|
||||||
def test_delete_dir(self):
|
def test_delete_dir(self):
|
||||||
base = self.cls(self.base)
|
base = self.cls(self.base)
|
||||||
base.joinpath('dirA')._delete()
|
base.joinpath('dirA')._delete()
|
||||||
self.assertRaises(FileNotFoundError, base.joinpath('dirA').stat)
|
self.assertFalse(base.joinpath('dirA').exists())
|
||||||
self.assertRaises(FileNotFoundError, base.joinpath('dirA', 'linkC').stat,
|
self.assertFalse(base.joinpath('dirA', 'linkC').exists(
|
||||||
follow_symlinks=False)
|
follow_symlinks=False))
|
||||||
base.joinpath('dirB')._delete()
|
base.joinpath('dirB')._delete()
|
||||||
self.assertRaises(FileNotFoundError, base.joinpath('dirB').stat)
|
self.assertFalse(base.joinpath('dirB').exists())
|
||||||
self.assertRaises(FileNotFoundError, base.joinpath('dirB', 'fileB').stat)
|
self.assertFalse(base.joinpath('dirB', 'fileB').exists())
|
||||||
self.assertRaises(FileNotFoundError, base.joinpath('dirB', 'linkD').stat,
|
self.assertFalse(base.joinpath('dirB', 'linkD').exists(
|
||||||
follow_symlinks=False)
|
follow_symlinks=False))
|
||||||
base.joinpath('dirC')._delete()
|
base.joinpath('dirC')._delete()
|
||||||
self.assertRaises(FileNotFoundError, base.joinpath('dirC').stat)
|
self.assertFalse(base.joinpath('dirC').exists())
|
||||||
self.assertRaises(FileNotFoundError, base.joinpath('dirC', 'dirD').stat)
|
self.assertFalse(base.joinpath('dirC', 'dirD').exists())
|
||||||
self.assertRaises(FileNotFoundError, base.joinpath('dirC', 'dirD', 'fileD').stat)
|
self.assertFalse(base.joinpath('dirC', 'dirD', 'fileD').exists())
|
||||||
self.assertRaises(FileNotFoundError, base.joinpath('dirC', 'fileC').stat)
|
self.assertFalse(base.joinpath('dirC', 'fileC').exists())
|
||||||
self.assertRaises(FileNotFoundError, base.joinpath('dirC', 'novel.txt').stat)
|
self.assertFalse(base.joinpath('dirC', 'novel.txt').exists())
|
||||||
|
|
||||||
def test_delete_missing(self):
|
def test_delete_missing(self):
|
||||||
tmp = self.cls(self.base, 'delete')
|
tmp = self.cls(self.base, 'delete')
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue