Skip to content

CliRunner(mix_stderr=False) does not capture all writes to stderr #2636

@RhetTbull

Description

@RhetTbull

When CliRunner(mix_stderr=False) is used, the output of print("foo", file=sys.stderr) is not captured. click.echo("foo", err=True) is captured (and this is tested in the test_stderr() test) but the output from print() is not captured.

Modifying tests/testing.py::test_stderr as follows demonstrates the bug:

def test_stderr():
    @click.command()
    def cli_stderr():
        click.echo("stdout")
        click.echo("stderr", err=True)
        print("sys.stderr", file=sys.stderr)

    runner = CliRunner(mix_stderr=False)

    result = runner.invoke(cli_stderr)

    assert result.output == "stdout\n"
    assert result.stdout == "stdout\n"
    assert result.stderr == "stderr\nsys.stderr\n"

    runner_mix = CliRunner(mix_stderr=True)
    result_mix = runner_mix.invoke(cli_stderr)

    assert result_mix.output == "stdout\nstderr\nsys.stderr\n"
    assert result_mix.stdout == "stdout\nstderr\nsys.stderr\n"

    with pytest.raises(ValueError):
        result_mix.stderr

    @click.command()
    def cli_empty_stderr():
        click.echo("stdout")

    runner = CliRunner(mix_stderr=False)

    result = runner.invoke(cli_empty_stderr)

    assert result.output == "stdout\n"
    assert result.stdout == "stdout\n"
    assert result.stderr == ""

Running this test, produces:

__________________________________________________________________________________ test_stderr ___________________________________________________________________________________

    def test_stderr():
        @click.command()
        def cli_stderr():
            click.echo("stdout")
            click.echo("stderr", err=True)
            print("sys.stderr", file=sys.stderr)

        runner = CliRunner(mix_stderr=False)

        result = runner.invoke(cli_stderr)

        assert result.output == "stdout\n"
        assert result.stdout == "stdout\n"
>       assert result.stderr == "stderr\nsys.stderr\n"
E       AssertionError: assert 'stderr\n' == 'stderr\nsys.stderr\n'
E           stderr
E         - sys.stderr

tests/test_testing.py:316: AssertionError
============================================================================ short test summary info =============================================================================
FAILED tests/test_testing.py::test_stderr - AssertionError: assert 'stderr\n' == 'stderr\nsys.stderr\n'
=============================================================================== 1 failed in 0.09s ================================================================================

The fix appears to be flusing stderr in src/click/testing.py:

--- a/src/click/testing.py
+++ b/src/click/testing.py
@@ -437,6 +437,7 @@ class CliRunner:
                 if self.mix_stderr:
                     stderr = None
                 else:
+                    sys.stderr.flush()
                     stderr = outstreams[1].getvalue()  # type: ignore

With this fix, and the modified test_stderr(), all tests pass.

Environment:

  • OS: macOS Ventura 13.5.1, Intel x86
  • Python version: Python 3.11.6 (v3.11.6:8b6ee5ba3b, Oct 2 2023, 11:18:21) [Clang 13.0.0 (clang-1300.0.29.30)] on darwin
  • Click version: version = "8.2.0.dev"

If you confirm this is a bug and not the intended behavior I am happy to submit a PR.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions