capture.py


Overview

The `capture.py` file implements a comprehensive per-test **stdout/stderr capturing mechanism** for pytest. Its primary role is to intercept and manage output streams (`sys.stdout`, `sys.stderr`, and optionally `sys.stdin`) during test execution, enabling tests to capture, inspect, and control their own output.

The file provides multiple capturing strategies, configurable via command-line options, including:

It manages capturing both at a **global level** during test collection and execution phases and at a **fixture level** when tests explicitly request capturing via fixtures like `capsys` or `capfd`.


Key Classes and Functions

1. pytest_addoption(parser: Parser) -> None

**Example Usage**:

pytest --capture=sys
pytest -s  # disables capturing

2. _colorama_workaround() -> None


3. _readline_workaround() -> None


4. _windowsconsoleio_workaround(stream: TextIO) -> None


5. CaptureBase (abstract base class)

**Usage**: Subclasses implement specific capturing strategies.


6. NoCapture(CaptureBase[str])


7. SysCaptureBase(CaptureBase[AnyStr])


8. SysCapture(SysCaptureBase[str])


9. SysCaptureBinary(SysCaptureBase[bytes])


10. FDCaptureBase(CaptureBase[AnyStr])


11. FDCapture(FDCaptureBase[str])


12. FDCaptureBinary(FDCaptureBase[bytes])


13. MultiCapture(Generic[AnyStr])


14. CaptureResult(NamedTuple, Generic[AnyStr])


15. CaptureManager


16. CaptureFixture(Generic[AnyStr])


17. Fixtures

The file exposes pytest fixtures to enable capturing in tests:

All fixtures:

**Example usage in tests:**

def test_output(capsys):
    print("hello")
    captured = capsys.readouterr()
    assert captured.out == "hello\n"

Important Implementation Details and Algorithms


Interactions with Other System Components


Visual Diagram: Class Structure in capture.py

classDiagram
    class CaptureBase~AnyStr~ {
        <<abstract>>
        +__init__(fd: int)
        +start()
        +done()
        +suspend()
        +resume()
        +writeorg(data: AnyStr)
        +snap() AnyStr
    }

    class NoCapture {
        +start()
        +done()
        +suspend()
        +resume()
        +writeorg(data: str)
        +snap() str
    }

    class SysCaptureBase~AnyStr~ {
        -_old: TextIO
        -tmpfile: TextIO
        -_state: str
        +start()
        +done()
        +suspend()
        +resume()
        +writeorg(data: AnyStr)
        +snap() AnyStr
    }

    class SysCapture {
        +snap() str
        +writeorg(data: str)
    }

    class SysCaptureBinary {
        +snap() bytes
        +writeorg(data: bytes)
    }

    class FDCaptureBase~AnyStr~ {
        -targetfd: int
        -targetfd_save: int
        -tmpfile: TextIO
        -syscapture: CaptureBase
        -_state: str
        +start()
        +done()
        +suspend()
        +resume()
        +writeorg(data: AnyStr)
        +snap() AnyStr
    }

    class FDCapture {
        +snap() str
        +writeorg(data: str)
    }

    class FDCaptureBinary {
        +snap() bytes
        +writeorg(data: bytes)
    }

    class MultiCapture~AnyStr~ {
        -in_: CaptureBase
        -out: CaptureBase
        -err: CaptureBase
        -_state: str
        +start_capturing()
        +stop_capturing()
        +suspend_capturing(in_: bool)
        +resume_capturing()
        +readouterr() CaptureResult
        +pop_outerr_to_orig()
    }

    class CaptureManager {
        -_method: str
        -_global_capturing: MultiCapture
        -_capture_fixture: CaptureFixture
        +start_global_capturing()
        +stop_global_capturing()
        +suspend_global_capture(in_: bool)
        +resume_global_capture()
        +set_fixture(capture_fixture: CaptureFixture)
        +unset_fixture()
        +activate_fixture()
        +deactivate_fixture()
        +suspend_fixture()
        +resume_fixture()
    }

    class CaptureFixture~AnyStr~ {
        -captureclass: type
        -request: SubRequest
        -_capture: MultiCapture
        -_captured_out: AnyStr
        -_captured_err: AnyStr
        +_start()
        +close()
        +readouterr() CaptureResult
        +_suspend()
        +_resume()
        +disabled()
    }

    CaptureBase <|-- NoCapture
    CaptureBase <|-- SysCaptureBase
    SysCaptureBase <|-- SysCapture
    SysCaptureBase <|-- SysCaptureBinary
    CaptureBase <|-- FDCaptureBase
    FDCaptureBase <|-- FDCapture
    FDCaptureBase <|-- FDCaptureBinary

Usage Examples

Using capsys Fixture in a Test

def test_print_output(capsys):
    print("hello world")
    captured = capsys.readouterr()
    assert captured.out == "hello world\n"
    assert captured.err == ""

Temporarily Disabling Capture Within a Test

def test_disable_capture(capsys):
    with capsys.disabled():
        print("this goes directly to terminal")
    captured = capsys.readouterr()
    assert "this goes directly to terminal" not in captured.out

Summary

`capture.py` is a central module in pytest that implements robust, flexible capturing of stdout/stderr streams during test runs. It supports multiple capturing backends, manages capturing state transitions, integrates with pytest lifecycle hooks, and provides user-facing fixtures to enable fine-grained control over output capture.

This functionality is crucial for enabling clean test output, debugging, and detailed test reporting.


End of Documentation for capture.py