Skip to content

Fix echo_via_pager closing sys.stdout when used with CliRunner#3494

Closed
jesustorres-code wants to merge 1 commit into
pallets:mainfrom
jesustorres-code:fix/pager-closes-stdout-in-cli-runner
Closed

Fix echo_via_pager closing sys.stdout when used with CliRunner#3494
jesustorres-code wants to merge 1 commit into
pallets:mainfrom
jesustorres-code:fix/pager-closes-stdout-in-cli-runner

Conversation

@jesustorres-code
Copy link
Copy Markdown

Fixes #3449

Root cause

MaybeStripAnsi subclasses io.TextIOWrapper and wraps sys.stdout.buffer. io.TextIOWrapper takes ownership of its underlying buffer and closes it when the wrapper is closed or garbage collected.

After echo_via_pager returns and the with get_pager_file() context exits, the MaybeStripAnsi instance goes out of scope. When Python GC collects it, TextIOWrapper.__del__ closes the buffer — which effectively closes sys.stdout. Then CliRunner.invoke's finally block calls sys.stdout.flush() and gets ValueError: I/O operation on closed file.

Fix

After stream.flush() in get_pager_file's finally block, call stream.detach() when the stream is a MaybeStripAnsi. detach() dissociates the TextIOWrapper from the buffer without closing it, so sys.stdout.buffer (and sys.stdout) remain open.

finally:
    stream.flush()
    if isinstance(stream, MaybeStripAnsi):
        stream.detach()

Test

Added test_echo_via_pager_in_cli_runner to tests/test_testing.py — a minimal reproduction of the reported bug that previously crashed with ValueError.

MaybeStripAnsi wraps the underlying binary buffer (sys.stdout.buffer) in
an io.TextIOWrapper subclass. TextIOWrapper takes ownership of its buffer
and closes it on GC, which silently closes sys.stdout. This caused
CliRunner.invoke() to raise ValueError: I/O operation on closed file when
it tried to flush sys.stdout in its finally block after the command ran.

Fix: detach the buffer from MaybeStripAnsi after flushing so the buffer
is not closed when the wrapper is garbage collected.

Fixes pallets#3449
@davidism davidism closed this May 21, 2026
@davidism
Copy link
Copy Markdown
Member

AI junk, ignored existing open PR

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.

"I/O operation on closed file" error with CliRunner and echo_via_pager under 8.4

2 participants