Skip to content

Commit bb38ae9

Browse files
authored
Merge pull request #7651 from bluetech/capture-safe-disable
capture: fix disabled()/global_and_fixture_disabled() enabling capturing when it was disabled
2 parents 6cf8933 + 0d5a650 commit bb38ae9

File tree

3 files changed

+51
-3
lines changed

3 files changed

+51
-3
lines changed

changelog/7148.bugfix.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Fixed ``--log-cli`` potentially causing unrelated ``print`` output to be swallowed.

src/_pytest/capture.py

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -592,7 +592,7 @@ def suspend_capturing(self, in_: bool = False) -> None:
592592
self._in_suspended = True
593593

594594
def resume_capturing(self) -> None:
595-
self._state = "resumed"
595+
self._state = "started"
596596
if self.out:
597597
self.out.resume()
598598
if self.err:
@@ -613,6 +613,10 @@ def stop_capturing(self) -> None:
613613
if self.in_:
614614
self.in_.done()
615615

616+
def is_started(self) -> bool:
617+
"""Whether actively capturing -- not suspended or stopped."""
618+
return self._state == "started"
619+
616620
def readouterr(self) -> CaptureResult[AnyStr]:
617621
if self.out:
618622
out = self.out.snap()
@@ -757,11 +761,19 @@ def resume_fixture(self) -> None:
757761
@contextlib.contextmanager
758762
def global_and_fixture_disabled(self) -> Generator[None, None, None]:
759763
"""Context manager to temporarily disable global and current fixture capturing."""
760-
self.suspend()
764+
do_fixture = self._capture_fixture and self._capture_fixture._is_started()
765+
if do_fixture:
766+
self.suspend_fixture()
767+
do_global = self._global_capturing and self._global_capturing.is_started()
768+
if do_global:
769+
self.suspend_global_capture()
761770
try:
762771
yield
763772
finally:
764-
self.resume()
773+
if do_global:
774+
self.resume_global_capture()
775+
if do_fixture:
776+
self.resume_fixture()
765777

766778
@contextlib.contextmanager
767779
def item_capture(self, when: str, item: Item) -> Generator[None, None, None]:
@@ -871,6 +883,12 @@ def _resume(self) -> None:
871883
if self._capture is not None:
872884
self._capture.resume_capturing()
873885

886+
def _is_started(self) -> bool:
887+
"""Whether actively capturing -- not disabled or closed."""
888+
if self._capture is not None:
889+
return self._capture.is_started()
890+
return False
891+
874892
@contextlib.contextmanager
875893
def disabled(self) -> Generator[None, None, None]:
876894
"""Temporarily disable capturing while inside the ``with`` block."""

testing/test_capture.py

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
from _pytest.capture import CaptureResult
1818
from _pytest.capture import MultiCapture
1919
from _pytest.config import ExitCode
20+
from _pytest.pytester import Testdir
2021

2122
# note: py.io capture tests where copied from
2223
# pylib 1.4.20.dev2 (rev 13d9af95547e)
@@ -640,6 +641,34 @@ def test_normal():
640641
else:
641642
result.stdout.no_fnmatch_line("*test_normal executed*")
642643

644+
def test_disabled_capture_fixture_twice(self, testdir: Testdir) -> None:
645+
"""Test that an inner disabled() exit doesn't undo an outer disabled().
646+
647+
Issue #7148.
648+
"""
649+
testdir.makepyfile(
650+
"""
651+
def test_disabled(capfd):
652+
print('captured before')
653+
with capfd.disabled():
654+
print('while capture is disabled 1')
655+
with capfd.disabled():
656+
print('while capture is disabled 2')
657+
print('while capture is disabled 1 after')
658+
print('captured after')
659+
assert capfd.readouterr() == ('captured before\\ncaptured after\\n', '')
660+
"""
661+
)
662+
result = testdir.runpytest_subprocess()
663+
result.stdout.fnmatch_lines(
664+
[
665+
"*while capture is disabled 1",
666+
"*while capture is disabled 2",
667+
"*while capture is disabled 1 after",
668+
],
669+
consecutive=True,
670+
)
671+
643672
@pytest.mark.parametrize("fixture", ["capsys", "capfd"])
644673
def test_fixture_use_by_other_fixtures(self, testdir, fixture):
645674
"""Ensure that capsys and capfd can be used by other fixtures during

0 commit comments

Comments
 (0)