pdb: resume capturing after `continue`
After `pdb.set_trace()` capturing is turned off. This patch resumes it after using the `continue` (or `c` / `cont`) command. Store _pytest_capman on the class, for pdbpp's do_debug hack to keep it. Without this, `debug …` would fail like this: /usr/lib/python3.6/cmd.py:217: in onecmd return func(arg) .venv/lib/python3.6/site-packages/pdb.py:608: in do_debug return orig_do_debug(self, arg) /usr/lib/python3.6/pdb.py:1099: in do_debug sys.call_tracing(p.run, (arg, globals, locals)) /usr/lib/python3.6/bdb.py:434: in run exec(cmd, globals, locals) /usr/lib/python3.6/bdb.py:51: in trace_dispatch return self.dispatch_line(frame) /usr/lib/python3.6/bdb.py:69: in dispatch_line self.user_line(frame) /usr/lib/python3.6/pdb.py:261: in user_line self.interaction(frame, None) .venv/lib/python3.6/site-packages/pdb.py:203: in interaction self.setup(frame, traceback) E AttributeError: 'PytestPdb' object has no attribute '_pytest_capman' - add pytest_leave_pdb hook - fixes test_pdb_interaction_capturing_twice: would fail on master now, but works here
This commit is contained in:
parent
65b97c2f41
commit
a4ea66cb1f
|
@ -0,0 +1 @@
|
||||||
|
Resume capturing output after ``continue`` with ``__import__("pdb").set_trace()``.
|
|
@ -96,8 +96,44 @@ class pytestPDB(object):
|
||||||
tw.line()
|
tw.line()
|
||||||
tw.sep(">", "PDB set_trace (IO-capturing turned off)")
|
tw.sep(">", "PDB set_trace (IO-capturing turned off)")
|
||||||
cls._pluginmanager.hook.pytest_enter_pdb(config=cls._config)
|
cls._pluginmanager.hook.pytest_enter_pdb(config=cls._config)
|
||||||
|
|
||||||
|
class _PdbWrapper(cls._pdb_cls, object):
|
||||||
|
_pytest_capman = capman
|
||||||
|
_continued = False
|
||||||
|
|
||||||
|
def do_continue(self, arg):
|
||||||
|
ret = super(_PdbWrapper, self).do_continue(arg)
|
||||||
|
if self._pytest_capman:
|
||||||
|
tw = _pytest.config.create_terminal_writer(cls._config)
|
||||||
|
tw.line()
|
||||||
|
tw.sep(">", "PDB continue (IO-capturing resumed)")
|
||||||
|
self._pytest_capman.resume_global_capture()
|
||||||
|
cls._pluginmanager.hook.pytest_leave_pdb(config=cls._config)
|
||||||
|
self._continued = True
|
||||||
|
return ret
|
||||||
|
|
||||||
|
do_c = do_cont = do_continue
|
||||||
|
|
||||||
|
def setup(self, f, tb):
|
||||||
|
"""Suspend on setup().
|
||||||
|
|
||||||
|
Needed after do_continue resumed, and entering another
|
||||||
|
breakpoint again.
|
||||||
|
"""
|
||||||
|
ret = super(_PdbWrapper, self).setup(f, tb)
|
||||||
|
if not ret and self._continued:
|
||||||
|
# pdb.setup() returns True if the command wants to exit
|
||||||
|
# from the interaction: do not suspend capturing then.
|
||||||
|
if self._pytest_capman:
|
||||||
|
self._pytest_capman.suspend_global_capture(in_=True)
|
||||||
|
return ret
|
||||||
|
|
||||||
|
_pdb = _PdbWrapper()
|
||||||
|
else:
|
||||||
|
_pdb = cls._pdb_cls()
|
||||||
|
|
||||||
if set_break:
|
if set_break:
|
||||||
cls._pdb_cls().set_trace(frame)
|
_pdb.set_trace(frame)
|
||||||
|
|
||||||
|
|
||||||
class PdbInvoke(object):
|
class PdbInvoke(object):
|
||||||
|
|
|
@ -609,3 +609,13 @@ def pytest_enter_pdb(config):
|
||||||
|
|
||||||
:param _pytest.config.Config config: pytest config object
|
:param _pytest.config.Config config: pytest config object
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
def pytest_leave_pdb(config):
|
||||||
|
""" called when leaving pdb (e.g. with continue after pdb.set_trace()).
|
||||||
|
|
||||||
|
Can be used by plugins to take special action just after the python
|
||||||
|
debugger leaves interactive mode.
|
||||||
|
|
||||||
|
:param _pytest.config.Config config: pytest config object
|
||||||
|
"""
|
||||||
|
|
|
@ -158,6 +158,7 @@ class TestPDB(object):
|
||||||
assert "= 1 failed in" in rest
|
assert "= 1 failed in" in rest
|
||||||
assert "def test_1" not in rest
|
assert "def test_1" not in rest
|
||||||
assert "Exit: Quitting debugger" in rest
|
assert "Exit: Quitting debugger" in rest
|
||||||
|
assert "PDB continue (IO-capturing resumed)" not in rest
|
||||||
self.flush(child)
|
self.flush(child)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
|
@ -489,18 +490,23 @@ class TestPDB(object):
|
||||||
"""
|
"""
|
||||||
)
|
)
|
||||||
child = testdir.spawn_pytest(str(p1))
|
child = testdir.spawn_pytest(str(p1))
|
||||||
|
child.expect(r"PDB set_trace \(IO-capturing turned off\)")
|
||||||
child.expect("test_1")
|
child.expect("test_1")
|
||||||
child.expect("x = 3")
|
child.expect("x = 3")
|
||||||
child.expect("Pdb")
|
child.expect("Pdb")
|
||||||
child.sendline("c")
|
child.sendline("c")
|
||||||
|
child.expect(r"PDB continue \(IO-capturing resumed\)")
|
||||||
|
child.expect(r"PDB set_trace \(IO-capturing turned off\)")
|
||||||
child.expect("x = 4")
|
child.expect("x = 4")
|
||||||
child.expect("Pdb")
|
child.expect("Pdb")
|
||||||
child.sendeof()
|
child.sendeof()
|
||||||
|
child.expect("_ test_1 _")
|
||||||
|
child.expect("def test_1")
|
||||||
|
child.expect("Captured stdout call")
|
||||||
rest = child.read().decode("utf8")
|
rest = child.read().decode("utf8")
|
||||||
assert "1 failed" in rest
|
|
||||||
assert "def test_1" in rest
|
|
||||||
assert "hello17" in rest # out is captured
|
assert "hello17" in rest # out is captured
|
||||||
assert "hello18" in rest # out is captured
|
assert "hello18" in rest # out is captured
|
||||||
|
assert "1 failed" in rest
|
||||||
self.flush(child)
|
self.flush(child)
|
||||||
|
|
||||||
def test_pdb_used_outside_test(self, testdir):
|
def test_pdb_used_outside_test(self, testdir):
|
||||||
|
@ -541,15 +547,19 @@ class TestPDB(object):
|
||||||
["E NameError: *xxx*", "*! *Exit: Quitting debugger !*"] # due to EOF
|
["E NameError: *xxx*", "*! *Exit: Quitting debugger !*"] # due to EOF
|
||||||
)
|
)
|
||||||
|
|
||||||
def test_enter_pdb_hook_is_called(self, testdir):
|
def test_enter_leave_pdb_hooks_are_called(self, testdir):
|
||||||
testdir.makeconftest(
|
testdir.makeconftest(
|
||||||
"""
|
"""
|
||||||
|
def pytest_configure(config):
|
||||||
|
config.testing_verification = 'configured'
|
||||||
|
|
||||||
def pytest_enter_pdb(config):
|
def pytest_enter_pdb(config):
|
||||||
assert config.testing_verification == 'configured'
|
assert config.testing_verification == 'configured'
|
||||||
print('enter_pdb_hook')
|
print('enter_pdb_hook')
|
||||||
|
|
||||||
def pytest_configure(config):
|
def pytest_leave_pdb(config):
|
||||||
config.testing_verification = 'configured'
|
assert config.testing_verification == 'configured'
|
||||||
|
print('leave_pdb_hook')
|
||||||
"""
|
"""
|
||||||
)
|
)
|
||||||
p1 = testdir.makepyfile(
|
p1 = testdir.makepyfile(
|
||||||
|
@ -558,11 +568,17 @@ class TestPDB(object):
|
||||||
|
|
||||||
def test_foo():
|
def test_foo():
|
||||||
pytest.set_trace()
|
pytest.set_trace()
|
||||||
|
assert 0
|
||||||
"""
|
"""
|
||||||
)
|
)
|
||||||
child = testdir.spawn_pytest(str(p1))
|
child = testdir.spawn_pytest(str(p1))
|
||||||
child.expect("enter_pdb_hook")
|
child.expect("enter_pdb_hook")
|
||||||
child.send("c\n")
|
child.sendline("c")
|
||||||
|
child.expect(r"PDB continue \(IO-capturing resumed\)")
|
||||||
|
child.expect("Captured stdout call")
|
||||||
|
rest = child.read().decode("utf8")
|
||||||
|
assert "leave_pdb_hook" in rest
|
||||||
|
assert "1 failed" in rest
|
||||||
child.sendeof()
|
child.sendeof()
|
||||||
self.flush(child)
|
self.flush(child)
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue