Skip to content

Commit ff04a1f

Browse files
authored
Merge pull request #4267 from nicoddemus/tmpdir-4262
Fix access denied error when deleting a stale temporary directory
2 parents 196a739 + f20eeeb commit ff04a1f

File tree

3 files changed

+36
-7
lines changed

3 files changed

+36
-7
lines changed

changelog/4262.bugfix.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Fix access denied error when deleting stale directories created by ``tmpdir`` / ``tmp_path``.

src/_pytest/pathlib.py

Lines changed: 17 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -186,19 +186,29 @@ def cleanup_on_exit(lock_path=lock_path, original_pid=pid):
186186

187187

188188
def maybe_delete_a_numbered_dir(path):
189-
"""removes a numbered directory if its lock can be obtained"""
189+
"""removes a numbered directory if its lock can be obtained and it does not seem to be in use"""
190+
lock_path = None
190191
try:
191-
create_cleanup_lock(path)
192+
lock_path = create_cleanup_lock(path)
193+
parent = path.parent
194+
195+
garbage = parent.joinpath("garbage-{}".format(uuid.uuid4()))
196+
path.rename(garbage)
197+
rmtree(garbage, force=True)
192198
except (OSError, EnvironmentError):
193199
# known races:
194200
# * other process did a cleanup at the same time
195201
# * deletable folder was found
202+
# * process cwd (Windows)
196203
return
197-
parent = path.parent
198-
199-
garbage = parent.joinpath("garbage-{}".format(uuid.uuid4()))
200-
path.rename(garbage)
201-
rmtree(garbage, force=True)
204+
finally:
205+
# if we created the lock, ensure we remove it even if we failed
206+
# to properly remove the numbered dir
207+
if lock_path is not None:
208+
try:
209+
lock_path.unlink()
210+
except (OSError, IOError):
211+
pass
202212

203213

204214
def ensure_deletable(path, consider_lock_dead_if_created_before):

testing/test_paths.py renamed to testing/test_pathlib.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,9 @@
44

55
import pytest
66
from _pytest.pathlib import fnmatch_ex
7+
from _pytest.pathlib import get_lock_path
8+
from _pytest.pathlib import maybe_delete_a_numbered_dir
9+
from _pytest.pathlib import Path
710

811

912
class TestPort:
@@ -66,3 +69,18 @@ def test_matching(self, match, pattern, path):
6669
)
6770
def test_not_matching(self, match, pattern, path):
6871
assert not match(pattern, path)
72+
73+
74+
def test_access_denied_during_cleanup(tmp_path, monkeypatch):
75+
"""Ensure that deleting a numbered dir does not fail because of OSErrors (#4262)."""
76+
path = tmp_path / "temp-1"
77+
path.mkdir()
78+
79+
def renamed_failed(*args):
80+
raise OSError("access denied")
81+
82+
monkeypatch.setattr(Path, "rename", renamed_failed)
83+
84+
lock_path = get_lock_path(path)
85+
maybe_delete_a_numbered_dir(path)
86+
assert not lock_path.is_file()

0 commit comments

Comments
 (0)