Skip to content

fix: preserve capture fixture output on thread timeouts - #212

Open
Arham-Begani wants to merge 1 commit into
pytest-dev:mainfrom
Arham-Begani:fix/fixture-capture-timeout-output
Open

Arham-Begani wants to merge 1 commit into
pytest-dev:mainfrom
Arham-Begani:fix/fixture-capture-timeout-output

Conversation

@Arham-Begani

Copy link
Copy Markdown

Fixes #172.

Problem

timeout_timer() suspends only global capture and reads only that buffer. A test using capsys, capfd, capsysbinary or capfdbinary keeps its output in the fixture's buffer. The process then exits without fixture teardown, discarding the output that could explain why the test hung. With --capture=no, an active capfd fixture can also capture the timeout diagnostics themselves.

This is reproducible on current main 1bbaf767df44cacdb401aba5490ffa7b97fd7337: print distinct stdout/stderr markers through a capture fixture, then sleep with --timeout=1 --timeout-method=thread. Neither marker survives the timeout.

Change

  • Deactivate the active capture fixture before reading global capture, so pytest flushes its buffered output to the enclosing capture stream.
  • Enter capturemanager.global_and_fixture_disabled() while writing diagnostics, flushing streams and exiting. An ExitStack keeps that context active through the existing exception handler and finally block and supports running without the capture plugin.
  • Preserve the existing stack dump and exit code 1.
  • Add a changelog entry and subprocess regressions for all four capture fixtures with global fd capture and --capture=no, plus a no-capture-plugin control.

The change is limited to the thread-timeout termination path.

Validation

Windows, CPython 3.13.15, pytest 9.1.1:

  • Before fix: 8 new subprocess cases fail; the no-capture-plugin control passes.
  • After fix: all 9 focused cases pass. These use a real timer, real capture fixtures and real process exit; os._exit is not mocked.
  • Full suite: 44 passed, 24 skipped in 117.27 seconds. Skips are the platform-specific SIGALRM / pexpect tests and the existing obsolete-function skip.
  • All applicable pinned pre-commit hooks pass, including Ruff, formatting, YAML checks and typos. The initial hook run normalized Windows checkout line endings; only the three related source/test/changelog files are in this PR.
  • git diff --check passes.

Fork CI: https://github.com/Arham-Begani/pytest-timeout/actions/runs/39199866683

All 10 matrix jobs passed: Python 3.10, 3.11, 3.12, 3.13 and 3.14 on Linux and Windows, including the Linux signal-based tests skipped locally.

The Python 3.12 Linux job reports 61 passed, 1 skipped and 6 expected failures; Windows reports 44 passed and 24 skipped. Checkout logs confirm both jobs used 2252ccb.

The fork's workflow definition adds a manual dispatch input, and its checkout step explicitly selects source commit 2252ccb0d3ac8419923040857ef9c46dfddd5ad6. That trigger-only change lives on the fork's main branch; it is not part of this PR. The workflow otherwise runs the project's Python 3.10–3.14 matrix on Linux and Windows.

AI assistance

An AI coding assistant reproduced, implemented, tested, reviewed and drafted this contribution with the account owner's authorization. No human review or personal authorship certification is claimed. The issue was checked for competing work and the reproduction/claim was posted before implementation.

@Arham-Begani

Copy link
Copy Markdown
Author

Could a maintainer review the focused thread-timeout capture fix when convenient? At 2252ccb0d3ac8419923040857ef9c46dfddd5ad6, all ten upstream Python 3.10–3.14 Linux/Windows jobs pass.

The change preserves output held by capsys/capfd and their binary variants before the process exits, including --capture=no, while keeping the existing stack dump and exit code. The nine subprocess regressions use the real timer and process exit; eight reproduce the loss before the fix, and the no-capture-plugin control already passes. The scope stays limited to thread-timeout teardown.

No additional code change is proposed here. The implementation, validation and this follow-up used AI assistance, as disclosed in the PR. Thanks for considering the review.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Suggestion: Using capturemanager.global_and_fixture_disabled

1 participant