From ae2fc277991f1717463f80aa98c57fd1f7718a87 Mon Sep 17 00:00:00 2001 From: Ronny Pfannschmidt Date: Wed, 24 Oct 2018 07:45:53 +0200 Subject: [PATCH] handle test folder cleanup being unable to create a cleanup lock --- changelog/4181.bugfix.rst | 1 + src/_pytest/pathlib.py | 14 ++++++++++---- testing/test_tmpdir.py | 9 ++++++++- 3 files changed, 19 insertions(+), 5 deletions(-) create mode 100644 changelog/4181.bugfix.rst diff --git a/changelog/4181.bugfix.rst b/changelog/4181.bugfix.rst new file mode 100644 index 000000000..668f8ba1e --- /dev/null +++ b/changelog/4181.bugfix.rst @@ -0,0 +1 @@ +Handle race condition between creation and deletion of temporary folders. diff --git a/src/_pytest/pathlib.py b/src/_pytest/pathlib.py index 081fce904..85db5d674 100644 --- a/src/_pytest/pathlib.py +++ b/src/_pytest/pathlib.py @@ -182,9 +182,15 @@ def register_cleanup_lock_removal(lock_path, register=atexit.register): 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())) @@ -214,7 +220,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): diff --git a/testing/test_tmpdir.py b/testing/test_tmpdir.py index 3c413e7c2..c033c2143 100644 --- a/testing/test_tmpdir.py +++ b/testing/test_tmpdir.py @@ -4,6 +4,7 @@ import sys import six import pytest +from _pytest import pathlib from _pytest.pathlib import Path @@ -284,11 +285,17 @@ class TestNumberedDir(object): 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