diff --git a/py/_plugin/pytest_capture.py b/py/_plugin/pytest_capture.py index 5004b9948..a3f0d437d 100644 --- a/py/_plugin/pytest_capture.py +++ b/py/_plugin/pytest_capture.py @@ -182,6 +182,8 @@ class CaptureManager: outerr = (item.outerr[0] + outerr[0], item.outerr[1] + outerr[1]) return outerr + if hasattr(item, 'outerr'): + return item.outerr return "", "" def activate_funcargs(self, pyfuncitem): diff --git a/py/_plugin/pytest_pdb.py b/py/_plugin/pytest_pdb.py index 7970213f8..1414586fc 100644 --- a/py/_plugin/pytest_pdb.py +++ b/py/_plugin/pytest_pdb.py @@ -10,10 +10,38 @@ def pytest_addoption(parser): action="store_true", dest="usepdb", default=False, help="start the interactive Python debugger on errors.") +def pytest_namespace(): + return {'set_trace': pytestPDB().set_trace} + def pytest_configure(config): if config.getvalue("usepdb"): - config.pluginmanager.register(PdbInvoke(), 'pdb') + config.pluginmanager.register(PdbInvoke(), 'pdbinvoke') +class pytestPDB: + """ Pseudo PDB that defers to the real pdb. """ + item = None + + def set_trace(self): + """ invoke PDB set_trace debugging, dropping any IO capturing. """ + frame = sys._getframe().f_back + item = getattr(self, 'item', None) + if item is not None: + capman = item.config.pluginmanager.getplugin("capturemanager") + out, err = capman.suspendcapture() + if hasattr(item, 'outerr'): + item.outerr = (item.outerr[0] + out, item.outerr[1] + err) + tw = py.io.TerminalWriter() + tw.line() + tw.sep(">", "PDB set_trace (IO-capturing turned off)") + py.std.pdb.Pdb().set_trace(frame) + +def pdbitem(item): + pytestPDB.item = item +pytest_runtest_setup = pytest_runtest_call = pytest_runtest_teardown = pdbitem + +def pytest_runtest_makereport(): + pytestPDB.item = None + class PdbInvoke: def pytest_sessionfinish(self, session): # don't display failures again at the end diff --git a/py/_plugin/pytest_pytester.py b/py/_plugin/pytest_pytester.py index fbf47e872..a020a3f83 100644 --- a/py/_plugin/pytest_pytester.py +++ b/py/_plugin/pytest_pytester.py @@ -351,13 +351,17 @@ class TmpTestdir: return self.runpybin("py.test", *args) def spawn_pytest(self, string, expect_timeout=10.0): - pexpect = py.test.importorskip("pexpect", "2.4") if self.request.config.getvalue("notoolsonpath"): py.test.skip("--no-tools-on-path prevents running pexpect-spawn tests") basetemp = self.tmpdir.mkdir("pexpect") invoke = self._getpybinargs("py.test")[0] cmd = "%s --basetemp=%s %s" % (invoke, basetemp, string) - child = pexpect.spawn(cmd, logfile=basetemp.join("spawn.out").open("w")) + return self.spawn(cmd, expect_timeout=expect_timeout) + + def spawn(self, cmd, expect_timeout=10.0): + pexpect = py.test.importorskip("pexpect", "2.4") + logfile = self.tmpdir.join("spawn.out") + child = pexpect.spawn(cmd, logfile=logfile.open("w")) child.timeout = expect_timeout return child diff --git a/testing/plugin/test_pytest_pdb.py b/testing/plugin/test_pytest_pdb.py index 0fbca26ef..1260b0724 100644 --- a/testing/plugin/test_pytest_pdb.py +++ b/testing/plugin/test_pytest_pdb.py @@ -1,4 +1,5 @@ import py +import sys class TestPDB: def pytest_funcarg__pdblist(self, request): @@ -83,3 +84,62 @@ class TestPDB: child.expect("1 failed") if child.isalive(): child.wait() + + def test_pdb_interaction_capturing_simple(self, testdir): + p1 = testdir.makepyfile(""" + import py + def test_1(): + i = 0 + print ("hello17") + py.test.set_trace() + x = 3 + """) + child = testdir.spawn_pytest(str(p1)) + child.expect("test_1") + child.expect("x = 3") + child.expect("(Pdb)") + child.sendeof() + rest = child.read() + assert "1 failed" in rest + assert "def test_1" in rest + assert "hello17" in rest # out is captured + if child.isalive(): + child.wait() + + def test_pdb_interaction_capturing_twice(self, testdir): + p1 = testdir.makepyfile(""" + import py + def test_1(): + i = 0 + print ("hello17") + py.test.set_trace() + x = 3 + print ("hello18") + py.test.set_trace() + x = 4 + """) + child = testdir.spawn_pytest(str(p1)) + child.expect("test_1") + child.expect("x = 3") + child.expect("(Pdb)") + child.sendline('c') + child.expect("x = 4") + child.sendeof() + rest = child.read() + assert "1 failed" in rest + assert "def test_1" in rest + assert "hello17" in rest # out is captured + assert "hello18" in rest # out is captured + if child.isalive(): + child.wait() + + def test_pdb_used_outside_test(self, testdir): + p1 = testdir.makepyfile(""" + import py + py.test.set_trace() + x = 5 + """) + child = testdir.spawn("%s %s" %(sys.executable, p1)) + child.expect("x = 5") + child.sendeof() + child.wait()