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:
		
							parent
							
								
									e9d18bd8ac
								
							
						
					
					
						commit
						9a879ee23e
					
				| 
						 | 
				
			
			@ -0,0 +1 @@
 | 
			
		|||
Fixed ``--log-cli`` potentially causing unrelated ``print`` output to be swallowed.
 | 
			
		||||
| 
						 | 
				
			
			@ -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."""
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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):
 | 
			
		||||
        """
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in New Issue