diff --git a/CHANGES.rst b/CHANGES.rst index 45e3daccb5..97f6a211b6 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -6,6 +6,7 @@ Version 8.2.1 - Fix flag value handling for flag options with a provided type. :issue:`2894` :issue:`2897` :pr:`2930` - Fix shell completion for nested groups. :issue:`2906` :pr:`2907` +- Flush ``sys.stderr`` at the end of ``CliRunner.invoke``. :issue:`2682` Version 8.2.0 ------------- diff --git a/src/click/testing.py b/src/click/testing.py index d19c103a9e..d9759e55bd 100644 --- a/src/click/testing.py +++ b/src/click/testing.py @@ -510,6 +510,7 @@ def invoke( exc_info = sys.exc_info() finally: sys.stdout.flush() + sys.stderr.flush() stdout = outstreams[0].getvalue() stderr = outstreams[1].getvalue() output = outstreams[2].getvalue() diff --git a/tests/test_testing.py b/tests/test_testing.py index 55d64701f4..0fd6973ae8 100644 --- a/tests/test_testing.py +++ b/tests/test_testing.py @@ -450,3 +450,24 @@ def test_isolation_stderr_errors(): click.echo("\udce2", err=True, nl=False) assert err.getvalue() == b"\\udce2" + + +def test_isolation_flushes_unflushed_stderr(): + """An un-flushed write to stderr, as with `print(..., file=sys.stderr)`, will end up + flushed by the runner at end of invocation. + """ + runner = CliRunner() + + with runner.isolation() as (_, err, _): + click.echo("\udce2", err=True, nl=False) + + assert err.getvalue() == b"\\udce2" + + @click.command() + def cli(): + # set end="", flush=False so that it's totally clear that we won't get any + # auto-flush behaviors + print("gyarados gyarados gyarados", file=sys.stderr, end="", flush=False) + + result = runner.invoke(cli) + assert result.stderr == "gyarados gyarados gyarados"