Skip to content

v0.5.2

Choose a tag to compare

@livingstaccato livingstaccato released this 09 Sep 04:12
· 2 commits to main since this release
855066a

Fixed

Pytest's stream capture now gives back the stream it took, not the one it saved.

SysCaptureBase.suspend handed sys.stdout back to the stream saved before
capture started, and resume reinstated the stream capture installed at
start. Neither read what was actually there, so a swap installed after capture
started — click.testing.CliRunner.isolation, contextlib.redirect_stdout, a
stream fixture of one's own — was discarded on the first suspend, and every
write after it landed in pytest's buffer instead of the caller's.

log_cli = true makes that ordinary rather than exotic: pytest's live-log
handler sits on the root logger and suspends global capture around each record.
One log line emitted from inside CliRunner.invoke, from any library at any
depth, left result.output empty while the command's output was real and showed
up under Captured stdout call. On one consumer's CLI suite that was 51 tests,
and the count understated it — assertions of the form
assert "--debug" not in result.output passed vacuously once the output was
empty.

Suspend now records the stream in place and resume returns it, which is what the
two methods already read as doing. Teardown is unchanged: done still restores
the stream capture replaced, so a swap left behind by a test cannot outlive it.
Consumers get this with no config change and keep log_cli.

The defect is pytest's, not this package's, and an upstream report follows;
_capture.py goes away when the pytest floor carries the fix. Reproduces with
nothing but the standard library:

def test_redirect_stdout_survives_a_log_record() -> None:
    captured = io.StringIO()
    with contextlib.redirect_stdout(captured):
        logging.getLogger("some.library").warning("a record reaches the root logger")
        print("inside the redirect")
    assert captured.getvalue() == "inside the redirect\n"