Suppress errors while removing tmpdir's lock files

Fix #5456
This commit is contained in:
Bruno Oliveira 2020-06-11 16:47:59 -03:00
parent 0821c5c81d
commit ab331c906e
3 changed files with 32 additions and 4 deletions

View File

@ -0,0 +1,2 @@
Fix a possible race condition when trying to remove lock files used to control access to folders
created by ``tmp_path`` and ``tmpdir``.

View File

@ -1,4 +1,5 @@
import atexit import atexit
import contextlib
import fnmatch import fnmatch
import itertools import itertools
import os import os
@ -290,9 +291,13 @@ def ensure_deletable(path: Path, consider_lock_dead_if_created_before: float) ->
return False return False
else: else:
if lock_time < consider_lock_dead_if_created_before: if lock_time < consider_lock_dead_if_created_before:
# wa want to ignore any errors while trying to remove the lock such as:
# - PermissionDenied, like the file permissions have changed since the lock creation
# - FileNotFoundError, in case another pytest process got here first.
# and any other cause of failure.
with contextlib.suppress(OSError):
lock.unlink() lock.unlink()
return True return True
else:
return False return False

View File

@ -1,9 +1,11 @@
import os.path import os.path
import sys import sys
import unittest.mock
import py import py
import pytest import pytest
from _pytest.pathlib import ensure_deletable
from _pytest.pathlib import fnmatch_ex from _pytest.pathlib import fnmatch_ex
from _pytest.pathlib import get_extended_length_path_str from _pytest.pathlib import get_extended_length_path_str
from _pytest.pathlib import get_lock_path from _pytest.pathlib import get_lock_path
@ -113,3 +115,22 @@ def test_get_extended_length_path_str():
assert get_extended_length_path_str(r"\\share\foo") == r"\\?\UNC\share\foo" assert get_extended_length_path_str(r"\\share\foo") == r"\\?\UNC\share\foo"
assert get_extended_length_path_str(r"\\?\UNC\share\foo") == r"\\?\UNC\share\foo" assert get_extended_length_path_str(r"\\?\UNC\share\foo") == r"\\?\UNC\share\foo"
assert get_extended_length_path_str(r"\\?\c:\foo") == r"\\?\c:\foo" assert get_extended_length_path_str(r"\\?\c:\foo") == r"\\?\c:\foo"
def test_suppress_error_removing_lock(tmp_path):
"""ensure_deletable should not raise an exception if the lock file cannot be removed (#5456)"""
path = tmp_path / "dir"
path.mkdir()
lock = get_lock_path(path)
lock.touch()
mtime = lock.stat().st_mtime
with unittest.mock.patch.object(Path, "unlink", side_effect=OSError):
assert not ensure_deletable(
path, consider_lock_dead_if_created_before=mtime + 30
)
assert lock.is_file()
# check now that we can remove the lock file in normal circumstances
assert ensure_deletable(path, consider_lock_dead_if_created_before=mtime + 30)
assert not lock.is_file()