Skip to content

Fix fixture finalizers skipped on KeyboardInterrupt during setup (#15067) - #15083

Open
Abelastro wants to merge 3 commits into
pytest-dev:mainfrom
Abelastro:add/fixture-finalizers-keyboard-interrupt
Open

Abelastro wants to merge 3 commits into
pytest-dev:mainfrom
Abelastro:add/fixture-finalizers-keyboard-interrupt

Conversation

@Abelastro

Copy link
Copy Markdown

What

Fix two defects that caused request.addfinalizer() finalizers to be silently skipped when fixture setup is interrupted by a BaseException such as KeyboardInterrupt:

  1. src/_pytest/runner.pyruntestprotocol() now runs the item teardown phase in a finally block, so teardown (and therefore fixture finalizers) executes even when setup or call re-raises an interruptible exception. Previously the KeyboardInterrupt propagated out of the setup phase and teardown never ran.
  2. src/_pytest/fixtures.pyFixtureDef.finish() only short-circuits when the fixture has no cached result and no registered finalizers. A setup interrupted by a BaseException has no cached result but may still hold pending finalizers; those must run and be cleared instead of being skipped and leaked.

Why

Per the documented behavior of request.addfinalizer(), a finalizer registered during setup must run at teardown no matter how setup completes. Repro:

@pytest.fixture
def resource(request):
    request.addfinalizer(lambda: Path("marker.txt").write_text("ran"))
    raise KeyboardInterrupt

Running this test previously aborted the session with !!!!!!!! KeyboardInterrupt !!!!!!!!!!! and left marker.txt missing. The finalizers are now executed ([#15067]).

Changes

  • src/_pytest/runner.py: teardown call moved into a finally (keeping the existing #11706 behavior for session fail/stop), reports initialized before the try.
  • src/_pytest/fixtures.py: finish() guard now if self.cached_result is None and not self._finalizers.
  • testing/python/fixtures.py: regression test test_finalizer_runs_when_setup_interrupted_by_keyboard_interrupt asserting the finalizer marker is written after runtestprotocol re-raises KeyboardInterrupt.
  • changelog/15067.bugfix.rst.

Verified against pytest's own suite: testing/test_runner.py + testing/python/fixtures.py (312 tests) pass with no new failures.

@psf-chronographer psf-chronographer Bot added the bot:chronographer:provided (automation) changelog entry is part of PR label Sep 22, 2026

This branch has not been deployed

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

Labels

bot:chronographer:provided (automation) changelog entry is part of PR

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant