Skip to content

Always call pytest_runtest_logfinish, even on Exit/KeyboardInterrupt - #15316

Open
doubledare704 wants to merge 3 commits into
pytest-dev:mainfrom
doubledare704:fix-logfinish-on-reraised-exception
Open

doubledare704 wants to merge 3 commits into
pytest-dev:mainfrom
doubledare704:fix-logfinish-on-reraised-exception

Conversation

@doubledare704

@doubledare704 doubledare704 commented Nov 1, 2026 •

Copy link
Copy Markdown

pytest_runtest_protocol calls logstart, then runtestprotocol, then logfinish with no try/finally. Exit (always) and KeyboardInterrupt (unless --pdb) are reraised out of runtestprotocol (see get_reraise_exceptions), so on pytest.exit() or Ctrl-C the current item gets a logstart but never a logfinish.

This was pointed out as a prerequisite in #1004 (comment): moving per-item terminal output to logfinish (for #1004 / #14761) would otherwise silently drop the last item's line on Ctrl-C. It's a latent bug on its own for any plugin that emits output or cleans up per-item state in logfinish.

Reproducer, with a conftest that prints from pytest_runtest_logfinish:

import pytest

def test_exit():
    pytest.exit("stop now")

Before: no logfinish output. After: logfinish runs, then the session exits as before.

The fix wraps runtestprotocol in try/finally. Added a parametrized test in test_runner.py for both pytest.exit() and KeyboardInterrupt, checking logstart/logfinish are paired for the interrupted item and that nothing after it starts.

  • Include new tests or update existing tests when applicable.
  • Allow maintainers to push and squash when merging my commits.
  • If AI agents were used, they are credited in Co-authored-by commit trailers.
  • Create a new changelog file in the changelog directory.
  • Add yourself to AUTHORS in alphabetical order.

🤖 Generated with Claude Code

Why this change uses try/finally instead of catching specific exceptions?

Exit and KeyboardInterrupt are deliberately reraised by CallInfo.from_call (via get_reraise_exceptions), so they escape runtestprotocol and skipped logfinish. finally keeps them propagating unchanged while guaranteeing the logstart/logfinish pairing. It doesn’t need to mirror the config-dependent reraise list, and it also covers exceptions raised from report hooks. Same pattern runtestprotocol already uses for clearing request state.

doubledare704 and others added 2 commits November 1, 2026 18:43
Exit (always) and KeyboardInterrupt (unless --pdb) are reraised out of
runtestprotocol, so pytest_runtest_protocol skipped the logfinish hook
and left the item's logstart unpaired. Plugins that emit output or clean
up state in logfinish silently lost the last item.

Wrap runtestprotocol in try/finally so logfinish is always called.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@pyba-chronographer pyba-chronographer Bot added the bot:chronographer:provided (automation) changelog entry is part of PR label Nov 1, 2026
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

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