Merge pull request #4347 from blueyed/pdb-recursive-capture
pdbpp: fix capturing with recursive debugging
This commit is contained in:
commit
e20e376881
|
@ -0,0 +1 @@
|
||||||
|
Fix output capturing when using pdb++ with recursive debugging.
|
|
@ -75,6 +75,7 @@ class pytestPDB(object):
|
||||||
_config = None
|
_config = None
|
||||||
_pdb_cls = pdb.Pdb
|
_pdb_cls = pdb.Pdb
|
||||||
_saved = []
|
_saved = []
|
||||||
|
_recursive_debug = 0
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def _init_pdb(cls, *args, **kwargs):
|
def _init_pdb(cls, *args, **kwargs):
|
||||||
|
@ -87,29 +88,37 @@ class pytestPDB(object):
|
||||||
capman.suspend_global_capture(in_=True)
|
capman.suspend_global_capture(in_=True)
|
||||||
tw = _pytest.config.create_terminal_writer(cls._config)
|
tw = _pytest.config.create_terminal_writer(cls._config)
|
||||||
tw.line()
|
tw.line()
|
||||||
# Handle header similar to pdb.set_trace in py37+.
|
if cls._recursive_debug == 0:
|
||||||
header = kwargs.pop("header", None)
|
# Handle header similar to pdb.set_trace in py37+.
|
||||||
if header is not None:
|
header = kwargs.pop("header", None)
|
||||||
tw.sep(">", header)
|
if header is not None:
|
||||||
elif capman and capman.is_globally_capturing():
|
tw.sep(">", header)
|
||||||
tw.sep(">", "PDB set_trace (IO-capturing turned off)")
|
elif capman and capman.is_globally_capturing():
|
||||||
else:
|
tw.sep(">", "PDB set_trace (IO-capturing turned off)")
|
||||||
tw.sep(">", "PDB set_trace")
|
else:
|
||||||
|
tw.sep(">", "PDB set_trace")
|
||||||
|
|
||||||
class _PdbWrapper(cls._pdb_cls, object):
|
class _PdbWrapper(cls._pdb_cls, object):
|
||||||
_pytest_capman = capman
|
_pytest_capman = capman
|
||||||
_continued = False
|
_continued = False
|
||||||
|
|
||||||
|
def do_debug(self, arg):
|
||||||
|
cls._recursive_debug += 1
|
||||||
|
ret = super(_PdbWrapper, self).do_debug(arg)
|
||||||
|
cls._recursive_debug -= 1
|
||||||
|
return ret
|
||||||
|
|
||||||
def do_continue(self, arg):
|
def do_continue(self, arg):
|
||||||
ret = super(_PdbWrapper, self).do_continue(arg)
|
ret = super(_PdbWrapper, self).do_continue(arg)
|
||||||
if self._pytest_capman:
|
if self._pytest_capman:
|
||||||
tw = _pytest.config.create_terminal_writer(cls._config)
|
tw = _pytest.config.create_terminal_writer(cls._config)
|
||||||
tw.line()
|
tw.line()
|
||||||
if self._pytest_capman.is_globally_capturing():
|
if cls._recursive_debug == 0:
|
||||||
tw.sep(">", "PDB continue (IO-capturing resumed)")
|
if self._pytest_capman.is_globally_capturing():
|
||||||
else:
|
tw.sep(">", "PDB continue (IO-capturing resumed)")
|
||||||
tw.sep(">", "PDB continue")
|
else:
|
||||||
self._pytest_capman.resume_global_capture()
|
tw.sep(">", "PDB continue")
|
||||||
|
self._pytest_capman.resume_global_capture()
|
||||||
cls._pluginmanager.hook.pytest_leave_pdb(
|
cls._pluginmanager.hook.pytest_leave_pdb(
|
||||||
config=cls._config, pdb=self
|
config=cls._config, pdb=self
|
||||||
)
|
)
|
||||||
|
|
|
@ -513,6 +513,76 @@ class TestPDB(object):
|
||||||
assert "1 failed" in rest
|
assert "1 failed" in rest
|
||||||
self.flush(child)
|
self.flush(child)
|
||||||
|
|
||||||
|
def test_pdb_interaction_continue_recursive(self, testdir):
|
||||||
|
p1 = testdir.makepyfile(
|
||||||
|
mytest="""
|
||||||
|
import pdb
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
count_continue = 0
|
||||||
|
|
||||||
|
# Simulates pdbpp, which injects Pdb into do_debug, and uses
|
||||||
|
# self.__class__ in do_continue.
|
||||||
|
class CustomPdb(pdb.Pdb, object):
|
||||||
|
def do_debug(self, arg):
|
||||||
|
import sys
|
||||||
|
import types
|
||||||
|
|
||||||
|
newglobals = {
|
||||||
|
'Pdb': self.__class__, # NOTE: different with pdb.Pdb
|
||||||
|
'sys': sys,
|
||||||
|
}
|
||||||
|
if sys.version_info < (3, ):
|
||||||
|
do_debug_func = pdb.Pdb.do_debug.im_func
|
||||||
|
else:
|
||||||
|
do_debug_func = pdb.Pdb.do_debug
|
||||||
|
|
||||||
|
orig_do_debug = types.FunctionType(
|
||||||
|
do_debug_func.__code__, newglobals,
|
||||||
|
do_debug_func.__name__, do_debug_func.__defaults__,
|
||||||
|
)
|
||||||
|
return orig_do_debug(self, arg)
|
||||||
|
do_debug.__doc__ = pdb.Pdb.do_debug.__doc__
|
||||||
|
|
||||||
|
def do_continue(self, *args, **kwargs):
|
||||||
|
global count_continue
|
||||||
|
count_continue += 1
|
||||||
|
return super(CustomPdb, self).do_continue(*args, **kwargs)
|
||||||
|
|
||||||
|
def foo():
|
||||||
|
print("print_from_foo")
|
||||||
|
|
||||||
|
def test_1():
|
||||||
|
i = 0
|
||||||
|
print("hello17")
|
||||||
|
pytest.set_trace()
|
||||||
|
x = 3
|
||||||
|
print("hello18")
|
||||||
|
|
||||||
|
assert count_continue == 2, "unexpected_failure: %d != 2" % count_continue
|
||||||
|
pytest.fail("expected_failure")
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
child = testdir.spawn_pytest("--pdbcls=mytest:CustomPdb %s" % str(p1))
|
||||||
|
child.expect(r"PDB set_trace \(IO-capturing turned off\)")
|
||||||
|
child.expect(r"\n\(Pdb")
|
||||||
|
child.sendline("debug foo()")
|
||||||
|
child.expect("ENTERING RECURSIVE DEBUGGER")
|
||||||
|
child.expect(r"\n\(\(Pdb")
|
||||||
|
child.sendline("c")
|
||||||
|
child.expect("LEAVING RECURSIVE DEBUGGER")
|
||||||
|
assert b"PDB continue" not in child.before
|
||||||
|
assert b"print_from_foo" in child.before
|
||||||
|
child.sendline("c")
|
||||||
|
child.expect(r"PDB continue \(IO-capturing resumed\)")
|
||||||
|
rest = child.read().decode("utf8")
|
||||||
|
assert "hello17" in rest # out is captured
|
||||||
|
assert "hello18" in rest # out is captured
|
||||||
|
assert "1 failed" in rest
|
||||||
|
assert "Failed: expected_failure" in rest
|
||||||
|
assert "AssertionError: unexpected_failure" not in rest
|
||||||
|
self.flush(child)
|
||||||
|
|
||||||
def test_pdb_without_capture(self, testdir):
|
def test_pdb_without_capture(self, testdir):
|
||||||
p1 = testdir.makepyfile(
|
p1 = testdir.makepyfile(
|
||||||
"""
|
"""
|
||||||
|
|
Loading…
Reference in New Issue