diff --git a/src/click/testing.py b/src/click/testing.py index 7c0e8741e2..04d263eeac 100644 --- a/src/click/testing.py +++ b/src/click/testing.py @@ -99,6 +99,18 @@ def __init__(self) -> None: self.stdout: io.BytesIO = BytesIOCopy(copy_to=self.output) self.stderr: io.BytesIO = BytesIOCopy(copy_to=self.output) + def __del__(self) -> None: + """ + Guarantee that embedded file-like objects are closed in a + predictable order, protecting against races between + self.output being closed and other streams being flushed on close + + .. versionadded:: 8.2.2 + """ + self.stderr.close() + self.stdout.close() + self.output.close() + class _NamedTextIOWrapper(io.TextIOWrapper): def __init__( diff --git a/tests/test_testing.py b/tests/test_testing.py index 0fd6973ae8..11fe29dc5d 100644 --- a/tests/test_testing.py +++ b/tests/test_testing.py @@ -448,8 +448,7 @@ def test_isolation_stderr_errors(): with runner.isolation() as (_, err, _): click.echo("\udce2", err=True, nl=False) - - assert err.getvalue() == b"\\udce2" + assert err.getvalue() == b"\\udce2" def test_isolation_flushes_unflushed_stderr(): @@ -460,8 +459,7 @@ def test_isolation_flushes_unflushed_stderr(): with runner.isolation() as (_, err, _): click.echo("\udce2", err=True, nl=False) - - assert err.getvalue() == b"\\udce2" + assert err.getvalue() == b"\\udce2" @click.command() def cli():