test_capture.py
Overview
The [test_capture.py](/projects/286/67356) file is a comprehensive test suite for the capturing facilities provided by the `_pytest.capture` module in the pytest testing framework. Capturing in pytest refers to intercepting output (stdout, stderr, and stdin) during test execution, allowing pytest to control, inspect, or suppress these streams for better test isolation and reporting.
This file validates the correctness, robustness, and integration of various capturing mechanisms including file descriptor capturing (`FDCapture`), system-level capturing (`SysCapture`), and combined capturing through `MultiCapture`. It also tests interactions with logging, fixture behavior, Unicode and binary output handling, error cases, and platform-specific workarounds.
Detailed Explanation of Key Components
Functions
StdCaptureFD(out: bool=True, err: bool=True, in_: bool=True) -> MultiCapture[str]
Creates a `MultiCapture` instance that captures standard input/output/error using file descriptor capturing (`FDCapture`) for fd 0 (stdin), 1 (stdout), and 2 (stderr), depending on the boolean flags.
Parameters:
out: Whether to capture stdout (fd 1).err: Whether to capture stderr (fd 2).in_: Whether to capture stdin (fd 0).
Returns:
MultiCapture[str]instance capturing the specified streams.Usage example:
cap = StdCaptureFD(out=True, err=True, in_=False) cap.start_capturing() print("hello") out, err = cap.readouterr() cap.stop_capturing() print(out) # "hello\n"
StdCapture(out: bool=True, err: bool=True, in_: bool=True) -> MultiCapture[str]
Creates a `MultiCapture` instance that captures standard streams using system-level capturing (`SysCapture`) on fd 0,1,2 depending on flags.
Parameters: Same as
StdCaptureFD.Returns:
MultiCapture[str].
TeeStdCapture(out: bool=True, err: bool=True, in_: bool=True) -> MultiCapture[str]
Creates a `MultiCapture` instance that captures output streams using system-level capturing (`SysCapture`) but with tee enabled, meaning captured output is also passed through to the original streams.
Parameters: Same as above.
Returns:
MultiCapture[str].
Classes
TestCaptureManager
Tests the `CaptureManager` class which manages global capturing modes (`no`, `sys`, and `fd`):
test_capturing_basic_api(self, method): Parametrized test covering starting, suspending, resuming, reading, and stopping global capture.test_init_capturing(self): Validates that starting capturing twice raises anAssertionError.
TestPerTestCapturing
Tests capturing on a per-test basis and interaction with setup and teardown fixtures:
Tests that output during setup, test call, and teardown are captured or displayed correctly.
Tests that no output carries over between tests.
Tests error output during teardown and module teardown.
TestLoggingInteraction
Tests interactions between capturing and Python's `logging` module:
Ensures logging output streams are properly owned and closed.
Tests logging behavior during setup, call, teardown phases.
Tests that logging output is captured or displayed correctly even with live logging enabled or capture disabled.
TestCaptureFixture
Tests the pytest fixtures `capsys` and `capfd` which provide capturing functionality to tests:
Validates standard functionality, binary capturing, fixture conflicts, and use inside other fixtures.
Tests disabling capture context managers.
Tests capturing results are accessible as attributes.
Tests capturing behavior on exceptions like
KeyboardInterrupt.
TestCaptureIO and TestTeeCaptureIO
Unit tests for `CaptureIO` and `TeeCaptureIO` classes which implement in-memory stream capturing:
Verify text output, Unicode handling, and byte buffer writes.
TestFDCapture
Tests for the `FDCapture` class which captures output at the file descriptor level:
Tests start/stop, suspend/resume, multiple captures, and invalid file descriptors.
Tests that the temporary capture file remains consistent.
TestStdCapture, TestTeeStdCapture, TestStdCaptureFD
These classes test `StdCapture`, `TeeStdCapture`, and `StdCaptureFD` respectively, verifying:
Basic capturing of stdout/stderr.
Behavior with only stdout or only stderr capturing.
Recursive capturing.
Restoring stdin.
Handling of newline characters.
Integration with logging.
Important Implementation Details
Uses pytest's parametrization to run tests across different capture methods (
fd,sys,tee-sys, andno).Uses
pytesterandpytestfixtures to create temporary test modules, run pytest subprocesses, and check output.Uses context managers to start and stop capturing cleanly.
Tests handle both text and binary capturing scenarios.
Contains platform-specific tests, including Windows console I/O workarounds.
Uses subprocess and
lsofchecks to ensure no file descriptor leaks during tests.Uses monkeypatching for environment manipulation during tests.
Handles edge cases such as partial setup failures, exceptions during capturing, and disabling capture mid-test.
Interaction With Other Parts of the System
Relies heavily on
_pytest.capturemodule classes and functions such asMultiCapture,FDCapture,SysCapture,CaptureManager, andCaptureIO.Uses pytest's core fixtures (
pytester,MonkeyPatch,CaptureFixture) to generate tests dynamically and to run pytest subprocesses.Validates integration with pytest's logging capture plugin.
Tests ensure capturing works seamlessly with pytest's test collection, setup, call, and teardown phases.
Interacts with system-level APIs (os.write, subprocess, file descriptors) to test low-level capturing.
Uses internal pytest APIs to manipulate and verify capture state and behavior.
Usage Examples
Using StdCapture to capture stdout and stderr
from test_capture import StdCapture
cap = StdCapture(out=True, err=True)
cap.start_capturing()
print("Hello stdout")
print("Hello stderr", file=sys.stderr)
out, err = cap.readouterr()
cap.stop_capturing()
assert out == "Hello stdout\n"
assert err == "Hello stderr\n"
Using capsys fixture in a pytest test
def test_output(capsys):
print("Output")
out, err = capsys.readouterr()
assert out == "Output\n"
assert err == ""
Disabling capture temporarily
def test_disabled_capture(capfd):
print("Captured before")
with capfd.disabled():
print("Not captured")
print("Captured after")
out, err = capfd.readouterr()
assert "Captured before" in out
assert "Captured after" in out
Mermaid Class Diagram
classDiagram
class TestCaptureManager {
+test_capturing_basic_api(method)
+test_init_capturing()
}
class TestPerTestCapturing {
+test_capture_and_fixtures(pytester)
+test_capture_scope_cache(pytester)
+test_no_carry_over(pytester)
+test_teardown_capturing(pytester)
+test_teardown_capturing_final(pytester)
+test_capturing_outerr(pytester)
}
class TestLoggingInteraction {
+test_logging_stream_ownership(pytester)
+test_logging_and_immediate_setupteardown(pytester)
+test_logging_and_crossscope_fixtures(pytester)
+test_conftestlogging_is_shown(pytester)
+test_conftestlogging_and_test_logging(pytester)
+test_logging_after_cap_stopped(pytester)
}
class TestCaptureFixture {
+test_std_functional(pytester, opt)
+test_capteesys(pytester)
+test_capsyscapfd(pytester)
+test_capturing_getfixturevalue(pytester)
+test_capsyscapfdbinary(pytester)
+test_capture_is_represented_on_failure_issue128(pytester, method)
+test_stdfd_functional(pytester)
+test_cafd_preserves_newlines(capfd, nl)
+test_capfdbinary(pytester)
+test_capsysbinary(pytester)
+test_partial_setup_failure(pytester)
+test_keyboardinterrupt_disables_capturing(pytester)
+test_capture_and_logging(pytester)
+test_disabled_capture_fixture(pytester, fixture, no_capture)
+test_disabled_capture_fixture_twice(pytester)
+test_fixture_use_by_other_fixtures(pytester, fixture)
+test_fixture_use_by_other_fixtures_teardown(pytester, cap)
}
class TestCaptureIO {
+test_text()
+test_unicode_and_str_mixture()
+test_write_bytes_to_buffer()
}
class TestTeeCaptureIO {
+test_text()
+test_unicode_and_str_mixture()
}
class TestFDCapture {
+test_simple(tmpfile)
+test_simple_many(tmpfile)
+test_simple_many_check_open_files(pytester)
+test_simple_fail_second_start(tmpfile)
+test_stderr()
+test_stdin()
+test_writeorg(tmpfile)
+test_simple_resume_suspend()
+test_capfd_sys_stdout_mode(capfd)
}
class TestStdCapture {
+test_capturing_done_simple()
+test_capturing_reset_simple()
+test_capturing_readouterr()
+test_capture_results_accessible_by_attribute()
+test_capturing_readouterr_unicode()
+test_reset_twice_error()
+test_capturing_modify_sysouterr_in_between()
+test_capturing_error_recursive()
+test_just_out_capture()
+test_just_err_capture()
+test_stdin_restored()
+test_stdin_nulled_by_default()
}
class TestTeeStdCapture {
+test_capturing_error_recursive()
}
class TestStdCaptureFD {
+test_simple_only_fd(pytester)
+test_intermingling()
+test_many(capfd)
}
TestTeeStdCapture --|> TestStdCapture
TestStdCaptureFD --|> TestStdCapture
Summary
The [test_capture.py](/projects/286/67356) file is a vital and extensive test suite for pytest's capture subsystem. It verifies that output capturing works correctly across multiple capture modes and scenarios, integrates properly with pytest's fixture and logging systems, handles binary and Unicode data, and behaves robustly under error conditions and platform-specific quirks.
This file ensures that pytest users can rely on consistent, isolated, and configurable capturing of test output streams, which is essential for clear test reporting and debugging.