Fix access denied error when deleting a stale temporary directory
Fix #4262
This commit is contained in:
parent
e6e40db9c7
commit
f20eeebde9
|
@ -0,0 +1 @@
|
||||||
|
Fix access denied error when deleting stale directories created by ``tmpdir`` / ``tmp_path``.
|
|
@ -186,19 +186,29 @@ def register_cleanup_lock_removal(lock_path, register=atexit.register):
|
||||||
|
|
||||||
|
|
||||||
def maybe_delete_a_numbered_dir(path):
|
def maybe_delete_a_numbered_dir(path):
|
||||||
"""removes a numbered directory if its lock can be obtained"""
|
"""removes a numbered directory if its lock can be obtained and it does not seem to be in use"""
|
||||||
|
lock_path = None
|
||||||
try:
|
try:
|
||||||
create_cleanup_lock(path)
|
lock_path = create_cleanup_lock(path)
|
||||||
|
parent = path.parent
|
||||||
|
|
||||||
|
garbage = parent.joinpath("garbage-{}".format(uuid.uuid4()))
|
||||||
|
path.rename(garbage)
|
||||||
|
rmtree(garbage, force=True)
|
||||||
except (OSError, EnvironmentError):
|
except (OSError, EnvironmentError):
|
||||||
# known races:
|
# known races:
|
||||||
# * other process did a cleanup at the same time
|
# * other process did a cleanup at the same time
|
||||||
# * deletable folder was found
|
# * deletable folder was found
|
||||||
|
# * process cwd (Windows)
|
||||||
return
|
return
|
||||||
parent = path.parent
|
finally:
|
||||||
|
# if we created the lock, ensure we remove it even if we failed
|
||||||
garbage = parent.joinpath("garbage-{}".format(uuid.uuid4()))
|
# to properly remove the numbered dir
|
||||||
path.rename(garbage)
|
if lock_path is not None:
|
||||||
rmtree(garbage, force=True)
|
try:
|
||||||
|
lock_path.unlink()
|
||||||
|
except (OSError, IOError):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
def ensure_deletable(path, consider_lock_dead_if_created_before):
|
def ensure_deletable(path, consider_lock_dead_if_created_before):
|
||||||
|
|
|
@ -4,6 +4,9 @@ import py
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
from _pytest.pathlib import fnmatch_ex
|
from _pytest.pathlib import fnmatch_ex
|
||||||
|
from _pytest.pathlib import get_lock_path
|
||||||
|
from _pytest.pathlib import maybe_delete_a_numbered_dir
|
||||||
|
from _pytest.pathlib import Path
|
||||||
|
|
||||||
|
|
||||||
class TestPort:
|
class TestPort:
|
||||||
|
@ -66,3 +69,18 @@ class TestPort:
|
||||||
)
|
)
|
||||||
def test_not_matching(self, match, pattern, path):
|
def test_not_matching(self, match, pattern, path):
|
||||||
assert not match(pattern, path)
|
assert not match(pattern, path)
|
||||||
|
|
||||||
|
|
||||||
|
def test_access_denied_during_cleanup(tmp_path, monkeypatch):
|
||||||
|
"""Ensure that deleting a numbered dir does not fail because of OSErrors (#4262)."""
|
||||||
|
path = tmp_path / "temp-1"
|
||||||
|
path.mkdir()
|
||||||
|
|
||||||
|
def renamed_failed(*args):
|
||||||
|
raise OSError("access denied")
|
||||||
|
|
||||||
|
monkeypatch.setattr(Path, "rename", renamed_failed)
|
||||||
|
|
||||||
|
lock_path = get_lock_path(path)
|
||||||
|
maybe_delete_a_numbered_dir(path)
|
||||||
|
assert not lock_path.is_file()
|
Loading…
Reference in New Issue