From d406786a8d935cddd9c8b1388535200205527c20 Mon Sep 17 00:00:00 2001 From: Daniel Hahler Date: Mon, 18 Mar 2019 22:58:22 +0100 Subject: [PATCH] pdb: handle capturing with fixtures only --- src/_pytest/capture.py | 18 +++++++++++++++ src/_pytest/debugging.py | 31 ++++++++++++++++++++----- testing/test_pdb.py | 49 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 92 insertions(+), 6 deletions(-) diff --git a/src/_pytest/capture.py b/src/_pytest/capture.py index c140757c4..3f6055bd8 100644 --- a/src/_pytest/capture.py +++ b/src/_pytest/capture.py @@ -107,6 +107,16 @@ class CaptureManager(object): return MultiCapture(out=False, err=False, in_=False) raise ValueError("unknown capturing method: %r" % method) # pragma: no cover + def is_capturing(self): + if self.is_globally_capturing(): + return "global" + capture_fixture = getattr(self._current_item, "_capture_fixture", None) + if capture_fixture is not None: + return ( + "fixture %s" % self._current_item._capture_fixture.request.fixturename + ) + return False + # Global capturing control def is_globally_capturing(self): @@ -134,6 +144,14 @@ class CaptureManager(object): if cap is not None: cap.suspend_capturing(in_=in_) + def suspend(self, in_=False): + self.suspend_fixture(self._current_item) + self.suspend_global_capture(in_) + + def resume(self): + self.resume_global_capture() + self.resume_fixture(self._current_item) + def read_global_capture(self): return self._global_capturing.readouterr() diff --git a/src/_pytest/debugging.py b/src/_pytest/debugging.py index 5b780d101..abe4f65b6 100644 --- a/src/_pytest/debugging.py +++ b/src/_pytest/debugging.py @@ -109,7 +109,7 @@ class pytestPDB(object): if cls._pluginmanager is not None: capman = cls._pluginmanager.getplugin("capturemanager") if capman: - capman.suspend_global_capture(in_=True) + capman.suspend(in_=True) tw = _pytest.config.create_terminal_writer(cls._config) tw.line() if cls._recursive_debug == 0: @@ -117,10 +117,22 @@ class pytestPDB(object): header = kwargs.pop("header", None) if header is not None: tw.sep(">", header) - elif capman and capman.is_globally_capturing(): - tw.sep(">", "PDB set_trace (IO-capturing turned off)") else: - tw.sep(">", "PDB set_trace") + if capman: + capturing = capman.is_capturing() + else: + capturing = False + if capturing: + if capturing == "global": + tw.sep(">", "PDB set_trace (IO-capturing turned off)") + else: + tw.sep( + ">", + "PDB set_trace (IO-capturing turned off for %s)" + % capturing, + ) + else: + tw.sep(">", "PDB set_trace") class _PdbWrapper(cls._pdb_cls, object): _pytest_capman = capman @@ -138,11 +150,18 @@ class pytestPDB(object): tw = _pytest.config.create_terminal_writer(cls._config) tw.line() if cls._recursive_debug == 0: - if self._pytest_capman.is_globally_capturing(): + capturing = self._pytest_capman.is_capturing() + if capturing == "global": tw.sep(">", "PDB continue (IO-capturing resumed)") + elif capturing: + tw.sep( + ">", + "PDB continue (IO-capturing resumed for %s)" + % capturing, + ) else: tw.sep(">", "PDB continue") - self._pytest_capman.resume_global_capture() + self._pytest_capman.resume() cls._pluginmanager.hook.pytest_leave_pdb( config=cls._config, pdb=self ) diff --git a/testing/test_pdb.py b/testing/test_pdb.py index 05cb9bd77..e6b5fecda 100644 --- a/testing/test_pdb.py +++ b/testing/test_pdb.py @@ -970,3 +970,52 @@ def test_quit_with_swallowed_SystemExit(testdir): rest = child.read().decode("utf8") assert "no tests ran" in rest TestPDB.flush(child) + + +@pytest.mark.parametrize("fixture", ("capfd", "capsys")) +def test_pdb_suspends_fixture_capturing(testdir, fixture): + """Using "-s" with pytest should suspend/resume fixture capturing.""" + p1 = testdir.makepyfile( + """ + def test_inner({fixture}): + import sys + + print("out_inner_before") + sys.stderr.write("err_inner_before\\n") + + __import__("pdb").set_trace() + + print("out_inner_after") + sys.stderr.write("err_inner_after\\n") + + out, err = {fixture}.readouterr() + assert out =="out_inner_before\\nout_inner_after\\n" + assert err =="err_inner_before\\nerr_inner_after\\n" + """.format( + fixture=fixture + ) + ) + + child = testdir.spawn_pytest(str(p1) + " -s") + + child.expect("Pdb") + before = child.before.decode("utf8") + assert ( + "> PDB set_trace (IO-capturing turned off for fixture %s) >" % (fixture) + in before + ) + + # Test that capturing is really suspended. + child.sendline("p 40 + 2") + child.expect("Pdb") + assert "\r\n42\r\n" in child.before.decode("utf8") + + child.sendline("c") + rest = child.read().decode("utf8") + assert "out_inner" not in rest + assert "err_inner" not in rest + + TestPDB.flush(child) + assert child.exitstatus == 0 + assert "= 1 passed in " in rest + assert "> PDB continue (IO-capturing resumed for fixture %s) >" % (fixture) in rest