Review rm_rf handling of FileNotFoundErrors (#6044)

Review rm_rf handling of FileNotFoundErrors
Conflicts:
 	 src/_pytest/pathlib.py
  	testing/test_tmpdir.py
This commit is contained in:
Bruno Oliveira 2019-10-23 19:22:26 -03:00
parent e89efa8325
commit 0084fd9783
3 changed files with 30 additions and 7 deletions

View File

@ -0,0 +1,3 @@
Properly ignore ``FileNotFoundError`` (``OSError.errno == NOENT`` in Python 2) exceptions when trying to remove old temporary directories,
for instance when multiple processes try to remove the same directory (common with ``pytest-xdist``
for example).

View File

@ -48,24 +48,38 @@ def ensure_reset_dir(path):
def on_rm_rf_error(func, path, exc, **kwargs): def on_rm_rf_error(func, path, exc, **kwargs):
"""Handles known read-only errors during rmtree.""" """Handles known read-only errors during rmtree.
The returned value is used only by our own tests.
"""
start_path = kwargs["start_path"] start_path = kwargs["start_path"]
excvalue = exc[1] exctype, excvalue = exc[:2]
# another process removed the file in the middle of the "rm_rf" (xdist for example)
# more context: https://github.com/pytest-dev/pytest/issues/5974#issuecomment-543799018
if isinstance(excvalue, OSError) and excvalue.errno == errno.ENOENT:
return False
if not isinstance(excvalue, OSError) or excvalue.errno not in ( if not isinstance(excvalue, OSError) or excvalue.errno not in (
errno.EACCES, errno.EACCES,
errno.EPERM, errno.EPERM,
): ):
warnings.warn( warnings.warn(
PytestWarning("(rm_rf) error removing {}: {}".format(path, excvalue)) PytestWarning(
"(rm_rf) error removing {}\n{}: {}".format(path, exctype, excvalue)
)
) )
return return False
if func not in (os.rmdir, os.remove, os.unlink): if func not in (os.rmdir, os.remove, os.unlink):
warnings.warn( warnings.warn(
PytestWarning("(rm_rf) error removing {}: {}".format(path, excvalue)) PytestWarning(
"(rm_rf) unknown function {} when removing {}:\n{}: {}".format(
path, func, exctype, excvalue
)
)
) )
return return False
# Chmod + retry. # Chmod + retry.
import stat import stat
@ -86,6 +100,7 @@ def on_rm_rf_error(func, path, exc, **kwargs):
chmod_rw(str(path)) chmod_rw(str(path))
func(path) func(path)
return True
def rm_rf(path): def rm_rf(path):

View File

@ -398,9 +398,14 @@ class TestRmRf:
on_rm_rf_error(os.unlink, str(fn), exc_info, start_path=tmp_path) on_rm_rf_error(os.unlink, str(fn), exc_info, start_path=tmp_path)
assert fn.is_file() assert fn.is_file()
# we ignore FileNotFoundError
file_not_found = OSError()
file_not_found.errno = errno.ENOENT
exc_info = (None, file_not_found, None)
assert not on_rm_rf_error(None, str(fn), exc_info, start_path=tmp_path)
permission_error = OSError() permission_error = OSError()
permission_error.errno = errno.EACCES permission_error.errno = errno.EACCES
# unknown function # unknown function
with pytest.warns(pytest.PytestWarning): with pytest.warns(pytest.PytestWarning):
exc_info = (None, permission_error, None) exc_info = (None, permission_error, None)