Fix terminal reporter output not appearing with capture active#13848
Open
RonnyPfannschmidt wants to merge 1 commit into
Open
Fix terminal reporter output not appearing with capture active#13848RonnyPfannschmidt wants to merge 1 commit into
RonnyPfannschmidt wants to merge 1 commit into
Conversation
bluetech
reviewed
Oct 27, 2025
bluetech
left a comment
Member
There was a problem hiding this comment.
I wonder how this compares to integrating with capture plugin e.g. using suspend_global_capture (or such)?
| # File descriptor for stdout, duplicated before capture starts. | ||
| # This allows the terminal reporter to bypass pytest's output capture (#8973). | ||
| # The FD is duplicated early in _prepareconfig before any capture can start. | ||
| stdout_fd_dup_key = StashKey[int]() |
Member
There was a problem hiding this comment.
It seems weird to me that this is in config/ when config doesn't really care about it. It should be in terminal.py or maybe capture.py if we want to take care of it "at the source".
| plugin = config.pluginmanager.get_plugin("terminalprogress") | ||
| assert plugin is not None | ||
| # Use a mock file with isatty returning True | ||
| from io import StringIO |
Member
There was a problem hiding this comment.
Better to have the imports at the beginning of the file if they don't need to be "lazy". Also below.
| def test_plugin_registration(self, pytester: pytest.Pytester) -> None: | ||
| """Test that the plugin is registered correctly on TTY output.""" | ||
| # The plugin module should be registered as a default plugin. | ||
| with patch.object(sys.stdout, "isatty", return_value=True): |
Member
There was a problem hiding this comment.
Can you explain why need to change this test?
| assert "\x1b]9;4;0;\x1b\\" in mock_file.getvalue() | ||
|
|
||
|
|
||
| def test_terminal_reporter_write_with_capture(pytester: Pytester) -> None: |
Member
There was a problem hiding this comment.
This test seems to pass also in main, so needs some refinement if we want to prevent regression.
RonnyPfannschmidt
force-pushed
the
fix-8973-terminalwriter-use-dup-if-necessary
branch
2 times, most recently
from
July 24, 2026 16:38
8ee87af to
5560219
Compare
Fixes pytest-dev#8973. Output written through the terminal reporter while output capture is active (e.g. by a plugin from within a test) used to disappear into the capture buffers. Duplicate stdout's file descriptor early in _prepareconfig, before any capture can start, and wrap it in an unbuffered text file stored in the config stash. The terminal reporter writes to this file, which always refers to the original stdout no matter how capture is started, stopped, or reconfigured - sidestepping capture entirely rather than toggling it. The regression test runs in a subprocess: in-process pytester runs replace stdout with an object without a real file descriptor, which takes the sys.stdout fallback path instead of the one under test. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
RonnyPfannschmidt
force-pushed
the
fix-8973-terminalwriter-use-dup-if-necessary
branch
from
July 24, 2026 16:55
5560219 to
314979a
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #8973
Output written through the terminal reporter while output capture is active (e.g. by a plugin from within a test, like pytest-print) used to silently disappear into the capture buffers. Plugins could only work around this by reaching into the private
CaptureManagerAPI to toggle capture around each write.Instead of toggling capture off and on around writes, the terminal reporter now sidesteps capture entirely:
_prepareconfig()duplicates stdout's file descriptor before any capture can start and wraps it in an unbuffered text file stored in the config stash (owned and closed by the config's cleanup). This file always refers to the original stdout — the same file capture restores on suspend — and remains writable no matter how capture is started, stopped, or reconfigured. Writing to it neither goes through capture nor affects capture state.To preserve output ordering with writes that legitimately reach the terminal through
sys.stdout(prints under-sorcapsys.disabled(), which are block-buffered when stdout is not a tty), the wrapper flushes stdout's buffers before each of its own writes; during active capture that flush merely moves pending test output into the capture buffers where it belongs.The regression test runs in a subprocess: in-process pytester runs replace stdout with an object without a real file descriptor, which takes the
sys.stdoutfallback path instead of the duplicated-fd path under test (the previous in-process regression test passed even without the fix).🤖 Generated with Claude Code