Merge pull request #7651 from bluetech/capture-safe-disable

capture: fix disabled()/global_and_fixture_disabled() enabling capturing when it was disabled

(cherry picked from commit bb38ae9c52)
This commit is contained in:
Ran Benita 2020-08-24 12:11:09 +03:00
parent e9d18bd8ac
commit 9a879ee23e
3 changed files with 51 additions and 3 deletions

View File

@ -0,0 +1 @@
Fixed ``--log-cli`` potentially causing unrelated ``print`` output to be swallowed.

View File

@ -537,7 +537,7 @@ class MultiCapture:
self._in_suspended = True
def resume_capturing(self) -> None:
self._state = "resumed"
self._state = "started"
if self.out:
self.out.resume()
if self.err:
@ -558,6 +558,10 @@ class MultiCapture:
if self.in_:
self.in_.done()
def is_started(self) -> bool:
"""Whether actively capturing -- not suspended or stopped."""
return self._state == "started"
def readouterr(self) -> CaptureResult:
if self.out:
out = self.out.snap()
@ -697,11 +701,19 @@ class CaptureManager:
@contextlib.contextmanager
def global_and_fixture_disabled(self) -> Generator[None, None, None]:
"""Context manager to temporarily disable global and current fixture capturing."""
self.suspend()
do_fixture = self._capture_fixture and self._capture_fixture._is_started()
if do_fixture:
self.suspend_fixture()
do_global = self._global_capturing and self._global_capturing.is_started()
if do_global:
self.suspend_global_capture()
try:
yield
finally:
self.resume()
if do_global:
self.resume_global_capture()
if do_fixture:
self.resume_fixture()
@contextlib.contextmanager
def item_capture(self, when: str, item: Item) -> Generator[None, None, None]:
@ -810,6 +822,12 @@ class CaptureFixture:
if self._capture is not None:
self._capture.resume_capturing()
def _is_started(self) -> bool:
"""Whether actively capturing -- not disabled or closed."""
if self._capture is not None:
return self._capture.is_started()
return False
@contextlib.contextmanager
def disabled(self) -> Generator[None, None, None]:
"""Temporarily disables capture while inside the 'with' block."""

View File

@ -16,6 +16,7 @@ from _pytest.capture import _get_multicapture
from _pytest.capture import CaptureManager
from _pytest.capture import MultiCapture
from _pytest.config import ExitCode
from _pytest.pytester import Testdir
# note: py.io capture tests where copied from
# pylib 1.4.20.dev2 (rev 13d9af95547e)
@ -633,6 +634,34 @@ class TestCaptureFixture:
else:
result.stdout.no_fnmatch_line("*test_normal executed*")
def test_disabled_capture_fixture_twice(self, testdir: Testdir) -> None:
"""Test that an inner disabled() exit doesn't undo an outer disabled().
Issue #7148.
"""
testdir.makepyfile(
"""
def test_disabled(capfd):
print('captured before')
with capfd.disabled():
print('while capture is disabled 1')
with capfd.disabled():
print('while capture is disabled 2')
print('while capture is disabled 1 after')
print('captured after')
assert capfd.readouterr() == ('captured before\\ncaptured after\\n', '')
"""
)
result = testdir.runpytest_subprocess()
result.stdout.fnmatch_lines(
[
"*while capture is disabled 1",
"*while capture is disabled 2",
"*while capture is disabled 1 after",
],
consecutive=True,
)
@pytest.mark.parametrize("fixture", ["capsys", "capfd"])
def test_fixture_use_by_other_fixtures(self, testdir, fixture):
"""