Skip to content

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

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jan 8, 2024
Merged
Show file tree
Hide file tree
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
8 changes: 4 additions & 4 deletions Lib/pathlib/_abc.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,6 @@
# Internals
#

# Maximum number of symlinks to follow in PathBase.resolve()
_MAX_SYMLINKS = 40

# Reference for Windows paths can be found at
# https://learn.microsoft.com/en-gb/windows/win32/fileio/naming-a-file .
_WIN_RESERVED_NAMES = frozenset(
Expand Down Expand Up @@ -500,6 +497,9 @@ class PathBase(PurePathBase):
"""
__slots__ = ()

# Maximum number of symlinks to follow in resolve()
_max_symlinks = 40

@classmethod
def _unsupported(cls, method_name):
msg = f"{cls.__name__}.{method_name}() is unsupported"
Expand Down Expand Up @@ -971,7 +971,7 @@ def resolve(self, strict=False):
# Like Linux and macOS, raise OSError(errno.ELOOP) if too many symlinks are
# encountered during resolution.
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))
target, target_parts = next_path.readlink()._split_stack()
# If the symlink target is absolute (like '/etc/hosts'), set the current
Expand Down
18 changes: 16 additions & 2 deletions Lib/test/test_pathlib/test_pathlib_abc.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import collections.abc
import collections
import io
import os
import errno
Expand Down Expand Up @@ -43,6 +43,8 @@ def test_pathmod(self):


class DummyPurePath(PurePathBase):
__slots__ = ()

def __eq__(self, other):
if not isinstance(other, DummyPurePath):
return NotImplemented
Expand Down Expand Up @@ -732,11 +734,18 @@ def close(self):
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):
"""
Simple implementation of PathBase that keeps files and directories in
memory.
"""
__slots__ = ()

_files = {}
_directories = {}
_symlinks = {}
Expand Down Expand Up @@ -765,7 +774,7 @@ def stat(self, *, follow_symlinks=True):
st_mode = stat.S_IFLNK
else:
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,
errors=None, newline=None):
Expand Down Expand Up @@ -1814,6 +1823,11 @@ def test_walk_symlink_location(self):


class DummyPathWithSymlinks(DummyPath):
__slots__ = ()

# Reduce symlink traversal limit to make tests run faster.
_max_symlinks = 20

def readlink(self):
path = str(self.parent.resolve() / self.name)
if path in self._symlinks:
Expand Down