Skip to content

Commit

Permalink
Merge pull request #4222 from RonnyPfannschmidt/pathlib-fixes
Browse files Browse the repository at this point in the history
handle race condition when creation and deletion of a numbered dir overlap
  • Loading branch information
nicoddemus authored Oct 26, 2018
2 parents 9cde67c + ae2fc27 commit d59786f
Show file tree
Hide file tree
Showing 3 changed files with 19 additions and 5 deletions.
1 change: 1 addition & 0 deletions changelog/4181.bugfix.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Handle race condition between creation and deletion of temporary folders.
14 changes: 10 additions & 4 deletions src/_pytest/pathlib.py
Original file line number Diff line number Diff line change
Expand Up @@ -185,9 +185,15 @@ def cleanup_on_exit(lock_path=lock_path, original_pid=pid):
return register(cleanup_on_exit)


def delete_a_numbered_dir(path):
"""removes a numbered directory"""
create_cleanup_lock(path)
def maybe_delete_a_numbered_dir(path):
"""removes a numbered directory if its lock can be obtained"""
try:
create_cleanup_lock(path)
except (OSError, EnvironmentError):
# known races:
# * other process did a cleanup at the same time
# * deletable folder was found
return
parent = path.parent

garbage = parent.joinpath("garbage-{}".format(uuid.uuid4()))
Expand Down Expand Up @@ -217,7 +223,7 @@ def ensure_deletable(path, consider_lock_dead_if_created_before):
def try_cleanup(path, consider_lock_dead_if_created_before):
"""tries to cleanup a folder if we can ensure its deletable"""
if ensure_deletable(path, consider_lock_dead_if_created_before):
delete_a_numbered_dir(path)
maybe_delete_a_numbered_dir(path)


def cleanup_candidates(root, prefix, keep):
Expand Down
9 changes: 8 additions & 1 deletion testing/test_tmpdir.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import six

import pytest
from _pytest import pathlib
from _pytest.pathlib import Path


Expand Down Expand Up @@ -287,11 +288,17 @@ def test_rmtree(self, tmp_path):
rmtree(adir, force=True)
assert not adir.exists()

def test_cleanup_symlink(self, tmp_path):
def test_cleanup_ignores_symlink(self, tmp_path):
the_symlink = tmp_path / (self.PREFIX + "current")
attempt_symlink_to(the_symlink, tmp_path / (self.PREFIX + "5"))
self._do_cleanup(tmp_path)

def test_removal_accepts_lock(self, tmp_path):
folder = pathlib.make_numbered_dir(root=tmp_path, prefix=self.PREFIX)
pathlib.create_cleanup_lock(folder)
pathlib.maybe_delete_a_numbered_dir(folder)
assert folder.is_dir()


def attempt_symlink_to(path, to_path):
"""Try to make a symlink from "path" to "to_path", skipping in case this platform
Expand Down

0 comments on commit d59786f

Please sign in to comment.