commit
						61f7c27ec0
					
				| 
						 | 
				
			
			@ -0,0 +1 @@
 | 
			
		|||
Python 3.12 support: fixed ``RuntimeError: TestResult has no addDuration method`` when running ``unittest`` tests.
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1 @@
 | 
			
		|||
Python 3.12 support: fixed ``shutil.rmtree(onerror=...)`` deprecation warning when using :fixture:`tmp_path`.
 | 
			
		||||
| 
						 | 
				
			
			@ -6,6 +6,7 @@ import itertools
 | 
			
		|||
import os
 | 
			
		||||
import shutil
 | 
			
		||||
import sys
 | 
			
		||||
import types
 | 
			
		||||
import uuid
 | 
			
		||||
import warnings
 | 
			
		||||
from enum import Enum
 | 
			
		||||
| 
						 | 
				
			
			@ -28,6 +29,8 @@ from typing import Iterable
 | 
			
		|||
from typing import Iterator
 | 
			
		||||
from typing import Optional
 | 
			
		||||
from typing import Set
 | 
			
		||||
from typing import Tuple
 | 
			
		||||
from typing import Type
 | 
			
		||||
from typing import TypeVar
 | 
			
		||||
from typing import Union
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -63,21 +66,33 @@ def get_lock_path(path: _AnyPurePath) -> _AnyPurePath:
 | 
			
		|||
    return path.joinpath(".lock")
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def on_rm_rf_error(func, path: str, exc, *, start_path: Path) -> bool:
 | 
			
		||||
def on_rm_rf_error(
 | 
			
		||||
    func,
 | 
			
		||||
    path: str,
 | 
			
		||||
    excinfo: Union[
 | 
			
		||||
        BaseException,
 | 
			
		||||
        Tuple[Type[BaseException], BaseException, Optional[types.TracebackType]],
 | 
			
		||||
    ],
 | 
			
		||||
    *,
 | 
			
		||||
    start_path: Path,
 | 
			
		||||
) -> bool:
 | 
			
		||||
    """Handle known read-only errors during rmtree.
 | 
			
		||||
 | 
			
		||||
    The returned value is used only by our own tests.
 | 
			
		||||
    """
 | 
			
		||||
    exctype, excvalue = exc[:2]
 | 
			
		||||
    if isinstance(excinfo, BaseException):
 | 
			
		||||
        exc = excinfo
 | 
			
		||||
    else:
 | 
			
		||||
        exc = excinfo[1]
 | 
			
		||||
 | 
			
		||||
    # 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, FileNotFoundError):
 | 
			
		||||
    if isinstance(exc, FileNotFoundError):
 | 
			
		||||
        return False
 | 
			
		||||
 | 
			
		||||
    if not isinstance(excvalue, PermissionError):
 | 
			
		||||
    if not isinstance(exc, PermissionError):
 | 
			
		||||
        warnings.warn(
 | 
			
		||||
            PytestWarning(f"(rm_rf) error removing {path}\n{exctype}: {excvalue}")
 | 
			
		||||
            PytestWarning(f"(rm_rf) error removing {path}\n{type(exc)}: {exc}")
 | 
			
		||||
        )
 | 
			
		||||
        return False
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -86,7 +101,7 @@ def on_rm_rf_error(func, path: str, exc, *, start_path: Path) -> bool:
 | 
			
		|||
            warnings.warn(
 | 
			
		||||
                PytestWarning(
 | 
			
		||||
                    "(rm_rf) unknown function {} when removing {}:\n{}: {}".format(
 | 
			
		||||
                        func, path, exctype, excvalue
 | 
			
		||||
                        func, path, type(exc), exc
 | 
			
		||||
                    )
 | 
			
		||||
                )
 | 
			
		||||
            )
 | 
			
		||||
| 
						 | 
				
			
			@ -149,7 +164,10 @@ def rm_rf(path: Path) -> None:
 | 
			
		|||
    are read-only."""
 | 
			
		||||
    path = ensure_extended_length_path(path)
 | 
			
		||||
    onerror = partial(on_rm_rf_error, start_path=path)
 | 
			
		||||
    shutil.rmtree(str(path), onerror=onerror)
 | 
			
		||||
    if sys.version_info >= (3, 12):
 | 
			
		||||
        shutil.rmtree(str(path), onexc=onerror)
 | 
			
		||||
    else:
 | 
			
		||||
        shutil.rmtree(str(path), onerror=onerror)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def find_prefixed(root: Path, prefix: str) -> Iterator[Path]:
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -298,6 +298,9 @@ class TestCaseFunction(Function):
 | 
			
		|||
    def stopTest(self, testcase: "unittest.TestCase") -> None:
 | 
			
		||||
        pass
 | 
			
		||||
 | 
			
		||||
    def addDuration(self, testcase: "unittest.TestCase", elapsed: float) -> None:
 | 
			
		||||
        pass
 | 
			
		||||
 | 
			
		||||
    def runtest(self) -> None:
 | 
			
		||||
        from _pytest.debugging import maybe_wrap_pytest_function_for_tracing
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -512,20 +512,20 @@ class TestRmRf:
 | 
			
		|||
 | 
			
		||||
        # unknown exception
 | 
			
		||||
        with pytest.warns(pytest.PytestWarning):
 | 
			
		||||
            exc_info1 = (None, RuntimeError(), None)
 | 
			
		||||
            exc_info1 = (RuntimeError, RuntimeError(), None)
 | 
			
		||||
            on_rm_rf_error(os.unlink, str(fn), exc_info1, start_path=tmp_path)
 | 
			
		||||
            assert fn.is_file()
 | 
			
		||||
 | 
			
		||||
        # we ignore FileNotFoundError
 | 
			
		||||
        exc_info2 = (None, FileNotFoundError(), None)
 | 
			
		||||
        exc_info2 = (FileNotFoundError, FileNotFoundError(), None)
 | 
			
		||||
        assert not on_rm_rf_error(None, str(fn), exc_info2, start_path=tmp_path)
 | 
			
		||||
 | 
			
		||||
        # unknown function
 | 
			
		||||
        with pytest.warns(
 | 
			
		||||
            pytest.PytestWarning,
 | 
			
		||||
            match=r"^\(rm_rf\) unknown function None when removing .*foo.txt:\nNone: ",
 | 
			
		||||
            match=r"^\(rm_rf\) unknown function None when removing .*foo.txt:\n<class 'PermissionError'>: ",
 | 
			
		||||
        ):
 | 
			
		||||
            exc_info3 = (None, PermissionError(), None)
 | 
			
		||||
            exc_info3 = (PermissionError, PermissionError(), None)
 | 
			
		||||
            on_rm_rf_error(None, str(fn), exc_info3, start_path=tmp_path)
 | 
			
		||||
            assert fn.is_file()
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -533,12 +533,12 @@ class TestRmRf:
 | 
			
		|||
        with warnings.catch_warnings():
 | 
			
		||||
            warnings.simplefilter("ignore")
 | 
			
		||||
            with pytest.warns(None) as warninfo:  # type: ignore[call-overload]
 | 
			
		||||
                exc_info4 = (None, PermissionError(), None)
 | 
			
		||||
                exc_info4 = PermissionError()
 | 
			
		||||
                on_rm_rf_error(os.open, str(fn), exc_info4, start_path=tmp_path)
 | 
			
		||||
                assert fn.is_file()
 | 
			
		||||
            assert not [x.message for x in warninfo]
 | 
			
		||||
 | 
			
		||||
        exc_info5 = (None, PermissionError(), None)
 | 
			
		||||
        exc_info5 = PermissionError()
 | 
			
		||||
        on_rm_rf_error(os.unlink, str(fn), exc_info5, start_path=tmp_path)
 | 
			
		||||
        assert not fn.is_file()
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in New Issue