Fix `tmp_path_retention_policy` crash when skipping from fixture (#10517)
Also uses the stash to save the test status. Fix #10502
This commit is contained in:
		
							parent
							
								
									56544c11b5
								
							
						
					
					
						commit
						99dfc19fe6
					
				| 
						 | 
				
			
			@ -5,11 +5,15 @@ import sys
 | 
			
		|||
import tempfile
 | 
			
		||||
from pathlib import Path
 | 
			
		||||
from shutil import rmtree
 | 
			
		||||
from typing import Dict
 | 
			
		||||
from typing import Generator
 | 
			
		||||
from typing import Optional
 | 
			
		||||
from typing import TYPE_CHECKING
 | 
			
		||||
from typing import Union
 | 
			
		||||
 | 
			
		||||
from _pytest.nodes import Item
 | 
			
		||||
from _pytest.stash import StashKey
 | 
			
		||||
 | 
			
		||||
if TYPE_CHECKING:
 | 
			
		||||
    from typing_extensions import Literal
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -33,6 +37,8 @@ from _pytest.fixtures import fixture
 | 
			
		|||
from _pytest.fixtures import FixtureRequest
 | 
			
		||||
from _pytest.monkeypatch import MonkeyPatch
 | 
			
		||||
 | 
			
		||||
tmppath_result_key = StashKey[Dict[str, bool]]()
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@final
 | 
			
		||||
@attr.s(init=False)
 | 
			
		||||
| 
						 | 
				
			
			@ -273,11 +279,15 @@ def tmp_path(
 | 
			
		|||
    # Remove the tmpdir if the policy is "failed" and the test passed.
 | 
			
		||||
    tmp_path_factory: TempPathFactory = request.session.config._tmp_path_factory  # type: ignore
 | 
			
		||||
    policy = tmp_path_factory._retention_policy
 | 
			
		||||
    if policy == "failed" and request.node._tmp_path_result_call.passed:
 | 
			
		||||
    result_dict = request.node.stash[tmppath_result_key]
 | 
			
		||||
 | 
			
		||||
    if policy == "failed" and result_dict.get("call", True):
 | 
			
		||||
        # We do a "best effort" to remove files, but it might not be possible due to some leaked resource,
 | 
			
		||||
        # permissions, etc, in which case we ignore it.
 | 
			
		||||
        rmtree(path, ignore_errors=True)
 | 
			
		||||
 | 
			
		||||
    del request.node.stash[tmppath_result_key]
 | 
			
		||||
 | 
			
		||||
    # remove dead symlink
 | 
			
		||||
    basetemp = tmp_path_factory._basetemp
 | 
			
		||||
    if basetemp is None:
 | 
			
		||||
| 
						 | 
				
			
			@ -306,7 +316,11 @@ def pytest_sessionfinish(session, exitstatus: Union[int, ExitCode]):
 | 
			
		|||
 | 
			
		||||
 | 
			
		||||
@hookimpl(tryfirst=True, hookwrapper=True)
 | 
			
		||||
def pytest_runtest_makereport(item, call):
 | 
			
		||||
def pytest_runtest_makereport(item: Item, call):
 | 
			
		||||
    outcome = yield
 | 
			
		||||
    result = outcome.get_result()
 | 
			
		||||
    setattr(item, "_tmp_path_result_" + result.when, result)
 | 
			
		||||
 | 
			
		||||
    if tmppath_result_key not in item.stash:
 | 
			
		||||
        item.stash[tmppath_result_key] = {result.when: result.passed}
 | 
			
		||||
    else:
 | 
			
		||||
        item.stash[tmppath_result_key][result.when] = result.passed
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -139,6 +139,70 @@ class TestConfigTmpPath:
 | 
			
		|||
            # Check the base dir itself is gone
 | 
			
		||||
            assert len(list(base_dir)) == 0
 | 
			
		||||
 | 
			
		||||
    # issue #10502
 | 
			
		||||
    def test_policy_failed_removes_dir_when_skipped_from_fixture(
 | 
			
		||||
        self, pytester: Pytester
 | 
			
		||||
    ) -> None:
 | 
			
		||||
        p = pytester.makepyfile(
 | 
			
		||||
            """
 | 
			
		||||
            import pytest
 | 
			
		||||
 | 
			
		||||
            @pytest.fixture
 | 
			
		||||
            def fixt(tmp_path):
 | 
			
		||||
                pytest.skip()
 | 
			
		||||
 | 
			
		||||
            def test_fixt(fixt):
 | 
			
		||||
                pass
 | 
			
		||||
        """
 | 
			
		||||
        )
 | 
			
		||||
        pytester.inline_run(p)
 | 
			
		||||
 | 
			
		||||
        # Check if the whole directory is removed
 | 
			
		||||
        root = pytester._test_tmproot
 | 
			
		||||
        for child in root.iterdir():
 | 
			
		||||
            base_dir = list(
 | 
			
		||||
                filter(lambda x: x.is_dir() and not x.is_symlink(), child.iterdir())
 | 
			
		||||
            )
 | 
			
		||||
            assert len(base_dir) == 0
 | 
			
		||||
 | 
			
		||||
    # issue #10502
 | 
			
		||||
    def test_policy_all_keeps_dir_when_skipped_from_fixture(
 | 
			
		||||
        self, pytester: Pytester
 | 
			
		||||
    ) -> None:
 | 
			
		||||
        p = pytester.makepyfile(
 | 
			
		||||
            """
 | 
			
		||||
            import pytest
 | 
			
		||||
 | 
			
		||||
            @pytest.fixture
 | 
			
		||||
            def fixt(tmp_path):
 | 
			
		||||
                pytest.skip()
 | 
			
		||||
 | 
			
		||||
            def test_fixt(fixt):
 | 
			
		||||
                pass
 | 
			
		||||
        """
 | 
			
		||||
        )
 | 
			
		||||
        pytester.makepyprojecttoml(
 | 
			
		||||
            """
 | 
			
		||||
            [tool.pytest.ini_options]
 | 
			
		||||
            tmp_path_retention_policy = "all"
 | 
			
		||||
        """
 | 
			
		||||
        )
 | 
			
		||||
        pytester.inline_run(p)
 | 
			
		||||
 | 
			
		||||
        # Check if the whole directory is kept
 | 
			
		||||
        root = pytester._test_tmproot
 | 
			
		||||
        for child in root.iterdir():
 | 
			
		||||
            base_dir = list(
 | 
			
		||||
                filter(lambda x: x.is_dir() and not x.is_symlink(), child.iterdir())
 | 
			
		||||
            )
 | 
			
		||||
            assert len(base_dir) == 1
 | 
			
		||||
            test_dir = list(
 | 
			
		||||
                filter(
 | 
			
		||||
                    lambda x: x.is_dir() and not x.is_symlink(), base_dir[0].iterdir()
 | 
			
		||||
                )
 | 
			
		||||
            )
 | 
			
		||||
            assert len(test_dir) == 1
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
testdata = [
 | 
			
		||||
    ("mypath", True),
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in New Issue