GH-113528: Speed up pathlib ABC tests. (#113788)

- Add `__slots__` to dummy path classes.
- Return namedtuple rather than `os.stat_result` from `DummyPath.stat()`.
- Reduce maximum symlink count in `DummyPathWithSymlinks.resolve()`.
This commit is contained in:
Barney Gale 2024-01-08 19:31:52 +00:00 committed by GitHub
parent bc71ae2b97
commit b3dba18eab
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 20 additions and 6 deletions

View file

@ -10,9 +10,6 @@ from stat import S_ISDIR, S_ISLNK, S_ISREG, S_ISSOCK, S_ISBLK, S_ISCHR, S_ISFIFO
# Internals # Internals
# #
# Maximum number of symlinks to follow in PathBase.resolve()
_MAX_SYMLINKS = 40
# Reference for Windows paths can be found at # Reference for Windows paths can be found at
# https://learn.microsoft.com/en-gb/windows/win32/fileio/naming-a-file . # https://learn.microsoft.com/en-gb/windows/win32/fileio/naming-a-file .
_WIN_RESERVED_NAMES = frozenset( _WIN_RESERVED_NAMES = frozenset(
@ -500,6 +497,9 @@ class PathBase(PurePathBase):
""" """
__slots__ = () __slots__ = ()
# Maximum number of symlinks to follow in resolve()
_max_symlinks = 40
@classmethod @classmethod
def _unsupported(cls, method_name): def _unsupported(cls, method_name):
msg = f"{cls.__name__}.{method_name}() is unsupported" msg = f"{cls.__name__}.{method_name}() is unsupported"
@ -971,7 +971,7 @@ class PathBase(PurePathBase):
# Like Linux and macOS, raise OSError(errno.ELOOP) if too many symlinks are # Like Linux and macOS, raise OSError(errno.ELOOP) if too many symlinks are
# encountered during resolution. # encountered during resolution.
link_count += 1 link_count += 1
if link_count >= _MAX_SYMLINKS: if link_count >= self._max_symlinks:
raise OSError(ELOOP, "Too many symbolic links in path", str(self)) raise OSError(ELOOP, "Too many symbolic links in path", str(self))
target, target_parts = next_path.readlink()._split_stack() target, target_parts = next_path.readlink()._split_stack()
# If the symlink target is absolute (like '/etc/hosts'), set the current # If the symlink target is absolute (like '/etc/hosts'), set the current

View file

@ -1,4 +1,4 @@
import collections.abc import collections
import io import io
import os import os
import errno import errno
@ -43,6 +43,8 @@ class PurePathBaseTest(unittest.TestCase):
class DummyPurePath(PurePathBase): class DummyPurePath(PurePathBase):
__slots__ = ()
def __eq__(self, other): def __eq__(self, other):
if not isinstance(other, DummyPurePath): if not isinstance(other, DummyPurePath):
return NotImplemented return NotImplemented
@ -660,11 +662,18 @@ 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
memory. memory.
""" """
__slots__ = ()
_files = {} _files = {}
_directories = {} _directories = {}
_symlinks = {} _symlinks = {}
@ -693,7 +702,7 @@ class DummyPath(PathBase):
st_mode = stat.S_IFLNK st_mode = stat.S_IFLNK
else: else:
raise FileNotFoundError(errno.ENOENT, "Not found", str(self)) raise FileNotFoundError(errno.ENOENT, "Not found", str(self))
return os.stat_result((st_mode, hash(str(self)), 0, 0, 0, 0, 0, 0, 0, 0)) return DummyPathStatResult(st_mode, hash(str(self)), 0, 0, 0, 0, 0, 0, 0, 0)
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):
@ -1728,6 +1737,11 @@ class DummyPathTest(DummyPurePathTest):
class DummyPathWithSymlinks(DummyPath): class DummyPathWithSymlinks(DummyPath):
__slots__ = ()
# Reduce symlink traversal limit to make tests run faster.
_max_symlinks = 20
def readlink(self): def readlink(self):
path = str(self.parent.resolve() / self.name) path = str(self.parent.resolve() / self.name)
if path in self._symlinks: if path in self._symlinks: