Skip to content

Commit

Permalink
Fix tmp_path regression introduced in 7.3.0 (#10911)
Browse files Browse the repository at this point in the history
The problem is that we would loop over all directories of the basetemp directory searching for dead symlinks, for each test, which would compound over the test session run.

Doing the cleanup just once, at the end of the session, fixes the problem.

Fix #10896
  • Loading branch information
nicoddemus committed Apr 14, 2023
1 parent b893d2a commit d380771
Show file tree
Hide file tree
Showing 3 changed files with 13 additions and 13 deletions.
1 change: 1 addition & 0 deletions changelog/10896.bugfix.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Fixed performance regression related to :fixture:`tmp_path` and the new :confval:`tmp_path_retention_policy` option.
4 changes: 2 additions & 2 deletions src/_pytest/pathlib.py
Original file line number Diff line number Diff line change
Expand Up @@ -353,7 +353,7 @@ def cleanup_candidates(root: Path, prefix: str, keep: int) -> Iterator[Path]:
yield path


def cleanup_dead_symlink(root: Path):
def cleanup_dead_symlinks(root: Path):
for left_dir in root.iterdir():
if left_dir.is_symlink():
if not left_dir.resolve().exists():
Expand All @@ -371,7 +371,7 @@ def cleanup_numbered_dir(
for path in root.glob("garbage-*"):
try_cleanup(path, consider_lock_dead_if_created_before)

cleanup_dead_symlink(root)
cleanup_dead_symlinks(root)


def make_numbered_dir_with_cleanup(
Expand Down
21 changes: 10 additions & 11 deletions src/_pytest/tmpdir.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
from .pathlib import make_numbered_dir
from .pathlib import make_numbered_dir_with_cleanup
from .pathlib import rm_rf
from .pathlib import cleanup_dead_symlink
from .pathlib import cleanup_dead_symlinks
from _pytest.compat import final, get_user_id
from _pytest.config import Config
from _pytest.config import ExitCode
Expand Down Expand Up @@ -289,31 +289,30 @@ def tmp_path(

del request.node.stash[tmppath_result_key]

# remove dead symlink
basetemp = tmp_path_factory._basetemp
if basetemp is None:
return
cleanup_dead_symlink(basetemp)


def pytest_sessionfinish(session, exitstatus: Union[int, ExitCode]):
"""After each session, remove base directory if all the tests passed,
the policy is "failed", and the basetemp is not specified by a user.
"""
tmp_path_factory: TempPathFactory = session.config._tmp_path_factory
if tmp_path_factory._basetemp is None:
basetemp = tmp_path_factory._basetemp
if basetemp is None:
return

policy = tmp_path_factory._retention_policy
if (
exitstatus == 0
and policy == "failed"
and tmp_path_factory._given_basetemp is None
):
passed_dir = tmp_path_factory._basetemp
if passed_dir.exists():
if basetemp.is_dir():
# We do a "best effort" to remove files, but it might not be possible due to some leaked resource,
# permissions, etc, in which case we ignore it.
rmtree(passed_dir, ignore_errors=True)
rmtree(basetemp, ignore_errors=True)

# Remove dead symlinks.
if basetemp.is_dir():
cleanup_dead_symlinks(basetemp)


@hookimpl(tryfirst=True, hookwrapper=True)
Expand Down

0 comments on commit d380771

Please sign in to comment.