Skip to content

tmp_path_retention_policy="failed" removes directories for setup and teardown errors #14998

Description

@aoto-tech
  • a detailed description of the bug or problem you are having
  • output of pip list from the virtual environment you are using
  • pytest and operating system versions
  • minimal example if possible

Description

With tmp_path_retention_policy = "failed", temporary directories are retained for failures during the test call, but removed when an error occurs during fixture setup or teardown.

The documentation describes "failed" as retaining directories for tests whose outcome is "error" or "failed", so I expected all three cases to be retained.

I reproduced this locally on main at commit 3fd8675d6d798507c06cf9c60753be6d9d7b0e17. I have not verified released versions.

Reproduction

pytest.ini:

[pytest]
tmp_path_retention_policy = failed

test_repro.py:

import pytest


def write_evidence(tmp_path, phase):
    evidence = tmp_path / "debug-evidence.txt"
    evidence.write_text(f"keep me: {phase}", encoding="utf-8")
    print(f"created[{phase}]={evidence} exists={evidence.exists()}")


@pytest.fixture
def broken_during_setup(tmp_path):
    write_evidence(tmp_path, "setup")
    raise RuntimeError("setup failed")


def test_setup_error(broken_during_setup):
    pass


def test_call_failure(tmp_path):
    write_evidence(tmp_path, "call")
    assert False


@pytest.fixture
def broken_during_teardown(tmp_path):
    write_evidence(tmp_path, "teardown")
    yield
    raise RuntimeError("teardown failed")


def test_teardown_error(broken_during_teardown):
    pass

Run:

python -m pytest -q -s --basetemp=retained test_repro.py

All three files exist immediately after creation:

created[setup]=...\test_setup_error0\debug-evidence.txt exists=True
created[call]=...\test_call_failure0\debug-evidence.txt exists=True
created[teardown]=...\test_teardown_error0\debug-evidence.txt exists=True

1 failed, 1 passed, 2 errors

After pytest exits, list the retained evidence:

python -c "from pathlib import Path; print(*Path('retained').rglob('debug-evidence.txt'), sep='\n')"

Actual result:

retained\test_call_failure0\debug-evidence.txt

The directories for the setup and teardown errors have been removed.

Expected result

All three directories should remain:

test_setup_error0
test_call_failure0
test_teardown_error0

Possible cause

The tmp_path finalizer currently bases cleanup primarily on:

result_dict.get("call", True)

For a setup error, the call phase does not exist, so the default value causes the directory to be removed.

For a teardown error, the tmp_path finalizer runs before the teardown report has been produced. If the call phase passed, it removes the directory without taking the teardown outcome into account.

Possible direction

Would it be appropriate to register the temporary path on the item and defer the cleanup decision until the teardown report is available?

Any fix should preserve the behavior covered by #10502: a test skipped during fixture setup, without an accompanying error, should not retain its tmp_path directory under the "failed" policy.

I would be happy to work on a fix after confirming the preferred lifecycle hook.

Environment

pytest 9.2.0.dev293+g3fd8675d6
commit 3fd8675d6d798507c06cf9c60753be6d9d7b0e17
Python 3.12.10
Microsoft Windows 11 Home 10.0.26200, 64-bit
pip list
attrs==26.1.0
certifi==2026.7.22
charset-normalizer==3.5.1
colorama==0.4.6
elementpath==5.1.4
execnet==2.1.2
hypothesis==6.168.0
idna==3.19
iniconfig==2.3.0
mock==5.2.0
numpy==2.5.3
packaging==26.3
pip==26.2.1
pluggy==1.6.0
Pygments==2.21.0
pytest==9.2.0.dev293+g3fd8675d6
pytest-xdist==3.8.0
PyYAML==6.0.3
requests==2.34.2
sortedcontainers==2.4.0
urllib3==2.7.0
xmlschema==4.3.2

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions