Skip to content

Commit 3edf417

Browse files
authored
[4.6] Review rm_rf handling of FileNotFoundErrors (#6050)
[4.6] Review rm_rf handling of FileNotFoundErrors
2 parents e89efa8 + 0084fd9 commit 3edf417

File tree

3 files changed

+30
-7
lines changed

3 files changed

+30
-7
lines changed

changelog/6044.bugfix.rst

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
Properly ignore ``FileNotFoundError`` (``OSError.errno == NOENT`` in Python 2) exceptions when trying to remove old temporary directories,
2+
for instance when multiple processes try to remove the same directory (common with ``pytest-xdist``
3+
for example).

src/_pytest/pathlib.py

Lines changed: 21 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -48,24 +48,38 @@ def ensure_reset_dir(path):
4848

4949

5050
def on_rm_rf_error(func, path, exc, **kwargs):
51-
"""Handles known read-only errors during rmtree."""
51+
"""Handles known read-only errors during rmtree.
52+
53+
The returned value is used only by our own tests.
54+
"""
5255
start_path = kwargs["start_path"]
53-
excvalue = exc[1]
56+
exctype, excvalue = exc[:2]
57+
58+
# another process removed the file in the middle of the "rm_rf" (xdist for example)
59+
# more context: https://github.com/pytest-dev/pytest/issues/5974#issuecomment-543799018
60+
if isinstance(excvalue, OSError) and excvalue.errno == errno.ENOENT:
61+
return False
5462

5563
if not isinstance(excvalue, OSError) or excvalue.errno not in (
5664
errno.EACCES,
5765
errno.EPERM,
5866
):
5967
warnings.warn(
60-
PytestWarning("(rm_rf) error removing {}: {}".format(path, excvalue))
68+
PytestWarning(
69+
"(rm_rf) error removing {}\n{}: {}".format(path, exctype, excvalue)
70+
)
6171
)
62-
return
72+
return False
6373

6474
if func not in (os.rmdir, os.remove, os.unlink):
6575
warnings.warn(
66-
PytestWarning("(rm_rf) error removing {}: {}".format(path, excvalue))
76+
PytestWarning(
77+
"(rm_rf) unknown function {} when removing {}:\n{}: {}".format(
78+
path, func, exctype, excvalue
79+
)
80+
)
6781
)
68-
return
82+
return False
6983

7084
# Chmod + retry.
7185
import stat
@@ -86,6 +100,7 @@ def chmod_rw(p):
86100
chmod_rw(str(path))
87101

88102
func(path)
103+
return True
89104

90105

91106
def rm_rf(path):

testing/test_tmpdir.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -398,9 +398,14 @@ def test_on_rm_rf_error(self, tmp_path):
398398
on_rm_rf_error(os.unlink, str(fn), exc_info, start_path=tmp_path)
399399
assert fn.is_file()
400400

401+
# we ignore FileNotFoundError
402+
file_not_found = OSError()
403+
file_not_found.errno = errno.ENOENT
404+
exc_info = (None, file_not_found, None)
405+
assert not on_rm_rf_error(None, str(fn), exc_info, start_path=tmp_path)
406+
401407
permission_error = OSError()
402408
permission_error.errno = errno.EACCES
403-
404409
# unknown function
405410
with pytest.warns(pytest.PytestWarning):
406411
exc_info = (None, permission_error, None)

0 commit comments

Comments
 (0)