pdb: move/refactor initialization of PytestPdbWrapper
This commit is contained in:
parent
b10f28949d
commit
61dfd0a94f
|
@ -81,6 +81,7 @@ class pytestPDB(object):
|
|||
_config = None
|
||||
_saved = []
|
||||
_recursive_debug = 0
|
||||
_wrapped_pdb_cls = None
|
||||
|
||||
@classmethod
|
||||
def _is_capturing(cls, capman):
|
||||
|
@ -89,16 +90,18 @@ class pytestPDB(object):
|
|||
return False
|
||||
|
||||
@classmethod
|
||||
def _import_pdb_cls(cls):
|
||||
def _import_pdb_cls(cls, capman):
|
||||
if not cls._config:
|
||||
# Happens when using pytest.set_trace outside of a test.
|
||||
return pdb.Pdb
|
||||
|
||||
pdb_cls = cls._config.getvalue("usepdb_cls")
|
||||
if not pdb_cls:
|
||||
return pdb.Pdb
|
||||
usepdb_cls = cls._config.getvalue("usepdb_cls")
|
||||
|
||||
modname, classname = pdb_cls
|
||||
if cls._wrapped_pdb_cls and cls._wrapped_pdb_cls[0] == usepdb_cls:
|
||||
return cls._wrapped_pdb_cls[1]
|
||||
|
||||
if usepdb_cls:
|
||||
modname, classname = usepdb_cls
|
||||
|
||||
try:
|
||||
__import__(modname)
|
||||
|
@ -109,43 +112,21 @@ class pytestPDB(object):
|
|||
pdb_cls = getattr(mod, parts[0])
|
||||
for part in parts[1:]:
|
||||
pdb_cls = getattr(pdb_cls, part)
|
||||
|
||||
return pdb_cls
|
||||
except Exception as exc:
|
||||
value = ":".join((modname, classname))
|
||||
raise UsageError("--pdbcls: could not import {!r}: {}".format(value, exc))
|
||||
|
||||
@classmethod
|
||||
def _init_pdb(cls, *args, **kwargs):
|
||||
""" Initialize PDB debugging, dropping any IO capturing. """
|
||||
import _pytest.config
|
||||
|
||||
if cls._pluginmanager is not None:
|
||||
capman = cls._pluginmanager.getplugin("capturemanager")
|
||||
if capman:
|
||||
capman.suspend(in_=True)
|
||||
tw = _pytest.config.create_terminal_writer(cls._config)
|
||||
tw.line()
|
||||
if cls._recursive_debug == 0:
|
||||
# Handle header similar to pdb.set_trace in py37+.
|
||||
header = kwargs.pop("header", None)
|
||||
if header is not None:
|
||||
tw.sep(">", header)
|
||||
else:
|
||||
capturing = cls._is_capturing(capman)
|
||||
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,
|
||||
raise UsageError(
|
||||
"--pdbcls: could not import {!r}: {}".format(value, exc)
|
||||
)
|
||||
else:
|
||||
tw.sep(">", "PDB set_trace")
|
||||
pdb_cls = pdb.Pdb
|
||||
|
||||
pdb_cls = cls._import_pdb_cls()
|
||||
wrapped_cls = cls._get_pdb_wrapper_class(pdb_cls, capman)
|
||||
cls._wrapped_pdb_cls = (usepdb_cls, wrapped_cls)
|
||||
return wrapped_cls
|
||||
|
||||
@classmethod
|
||||
def _get_pdb_wrapper_class(cls, pdb_cls, capman):
|
||||
import _pytest.config
|
||||
|
||||
class PytestPdbWrapper(pdb_cls, object):
|
||||
_pytest_capman = capman
|
||||
|
@ -177,9 +158,7 @@ class pytestPDB(object):
|
|||
capman.resume()
|
||||
else:
|
||||
tw.sep(">", "PDB continue")
|
||||
cls._pluginmanager.hook.pytest_leave_pdb(
|
||||
config=cls._config, pdb=self
|
||||
)
|
||||
cls._pluginmanager.hook.pytest_leave_pdb(config=cls._config, pdb=self)
|
||||
self._continued = True
|
||||
return ret
|
||||
|
||||
|
@ -221,24 +200,57 @@ class pytestPDB(object):
|
|||
if f is None:
|
||||
# Find last non-hidden frame.
|
||||
i = max(0, len(stack) - 1)
|
||||
while i and stack[i][0].f_locals.get(
|
||||
"__tracebackhide__", False
|
||||
):
|
||||
while i and stack[i][0].f_locals.get("__tracebackhide__", False):
|
||||
i -= 1
|
||||
return stack, i
|
||||
|
||||
_pdb = PytestPdbWrapper(**kwargs)
|
||||
cls._pluginmanager.hook.pytest_enter_pdb(config=cls._config, pdb=_pdb)
|
||||
return PytestPdbWrapper
|
||||
|
||||
@classmethod
|
||||
def _init_pdb(cls, method, *args, **kwargs):
|
||||
""" Initialize PDB debugging, dropping any IO capturing. """
|
||||
import _pytest.config
|
||||
|
||||
if cls._pluginmanager is not None:
|
||||
capman = cls._pluginmanager.getplugin("capturemanager")
|
||||
else:
|
||||
pdb_cls = cls._import_pdb_cls()
|
||||
_pdb = pdb_cls(**kwargs)
|
||||
capman = None
|
||||
if capman:
|
||||
capman.suspend(in_=True)
|
||||
|
||||
if cls._config:
|
||||
tw = _pytest.config.create_terminal_writer(cls._config)
|
||||
tw.line()
|
||||
|
||||
if cls._recursive_debug == 0:
|
||||
# Handle header similar to pdb.set_trace in py37+.
|
||||
header = kwargs.pop("header", None)
|
||||
if header is not None:
|
||||
tw.sep(">", header)
|
||||
else:
|
||||
capturing = cls._is_capturing(capman)
|
||||
if capturing == "global":
|
||||
tw.sep(">", "PDB %s (IO-capturing turned off)" % (method,))
|
||||
elif capturing:
|
||||
tw.sep(
|
||||
">",
|
||||
"PDB %s (IO-capturing turned off for %s)"
|
||||
% (method, capturing),
|
||||
)
|
||||
else:
|
||||
tw.sep(">", "PDB %s" % (method,))
|
||||
|
||||
_pdb = cls._import_pdb_cls(capman)(**kwargs)
|
||||
|
||||
if cls._pluginmanager:
|
||||
cls._pluginmanager.hook.pytest_enter_pdb(config=cls._config, pdb=_pdb)
|
||||
return _pdb
|
||||
|
||||
@classmethod
|
||||
def set_trace(cls, *args, **kwargs):
|
||||
"""Invoke debugging via ``Pdb.set_trace``, dropping any IO capturing."""
|
||||
frame = sys._getframe().f_back
|
||||
_pdb = cls._init_pdb(*args, **kwargs)
|
||||
_pdb = cls._init_pdb("set_trace", *args, **kwargs)
|
||||
_pdb.set_trace(frame)
|
||||
|
||||
|
||||
|
@ -265,7 +277,7 @@ class PdbTrace(object):
|
|||
|
||||
|
||||
def _test_pytest_function(pyfuncitem):
|
||||
_pdb = pytestPDB._init_pdb()
|
||||
_pdb = pytestPDB._init_pdb("runcall")
|
||||
testfunction = pyfuncitem.obj
|
||||
pyfuncitem.obj = _pdb.runcall
|
||||
if "func" in pyfuncitem._fixtureinfo.argnames: # pragma: no branch
|
||||
|
@ -315,7 +327,7 @@ def _postmortem_traceback(excinfo):
|
|||
|
||||
|
||||
def post_mortem(t):
|
||||
p = pytestPDB._init_pdb()
|
||||
p = pytestPDB._init_pdb("post_mortem")
|
||||
p.reset()
|
||||
p.interaction(None, t)
|
||||
if p.quitting:
|
||||
|
|
|
@ -638,36 +638,35 @@ class TestPDB(object):
|
|||
class pytestPDBTest(_pytest.debugging.pytestPDB):
|
||||
@classmethod
|
||||
def set_trace(cls, *args, **kwargs):
|
||||
# Init _PdbWrapper to handle capturing.
|
||||
_pdb = cls._init_pdb(*args, **kwargs)
|
||||
# Init PytestPdbWrapper to handle capturing.
|
||||
_pdb = cls._init_pdb("set_trace", *args, **kwargs)
|
||||
|
||||
# Mock out pdb.Pdb.do_continue.
|
||||
import pdb
|
||||
pdb.Pdb.do_continue = lambda self, arg: None
|
||||
|
||||
print("=== SET_TRACE ===")
|
||||
print("===" + " SET_TRACE ===")
|
||||
assert input() == "debug set_trace()"
|
||||
|
||||
# Simulate _PdbWrapper.do_debug
|
||||
# Simulate PytestPdbWrapper.do_debug
|
||||
cls._recursive_debug += 1
|
||||
print("ENTERING RECURSIVE DEBUGGER")
|
||||
print("=== SET_TRACE_2 ===")
|
||||
print("===" + " SET_TRACE_2 ===")
|
||||
|
||||
assert input() == "c"
|
||||
_pdb.do_continue("")
|
||||
print("=== SET_TRACE_3 ===")
|
||||
print("===" + " SET_TRACE_3 ===")
|
||||
|
||||
# Simulate _PdbWrapper.do_debug
|
||||
# Simulate PytestPdbWrapper.do_debug
|
||||
print("LEAVING RECURSIVE DEBUGGER")
|
||||
cls._recursive_debug -= 1
|
||||
|
||||
print("=== SET_TRACE_4 ===")
|
||||
print("===" + " SET_TRACE_4 ===")
|
||||
assert input() == "c"
|
||||
_pdb.do_continue("")
|
||||
|
||||
def do_continue(self, arg):
|
||||
print("=== do_continue")
|
||||
# _PdbWrapper.do_continue("")
|
||||
|
||||
monkeypatch.setattr(_pytest.debugging, "pytestPDB", pytestPDBTest)
|
||||
|
||||
|
@ -677,7 +676,7 @@ class TestPDB(object):
|
|||
set_trace()
|
||||
"""
|
||||
)
|
||||
child = testdir.spawn_pytest("%s %s" % (p1, capture_arg))
|
||||
child = testdir.spawn_pytest("--tb=short %s %s" % (p1, capture_arg))
|
||||
child.expect("=== SET_TRACE ===")
|
||||
before = child.before.decode("utf8")
|
||||
if not capture_arg:
|
||||
|
@ -1207,3 +1206,33 @@ def test_raises_bdbquit_with_eoferror(testdir):
|
|||
result = testdir.runpytest(str(p1))
|
||||
result.stdout.fnmatch_lines(["E *BdbQuit", "*= 1 failed in*"])
|
||||
assert result.ret == 1
|
||||
|
||||
|
||||
def test_pdb_wrapper_class_is_reused(testdir):
|
||||
p1 = testdir.makepyfile(
|
||||
"""
|
||||
def test():
|
||||
__import__("pdb").set_trace()
|
||||
__import__("pdb").set_trace()
|
||||
|
||||
import mypdb
|
||||
instances = mypdb.instances
|
||||
assert len(instances) == 2
|
||||
assert instances[0].__class__ is instances[1].__class__
|
||||
""",
|
||||
mypdb="""
|
||||
instances = []
|
||||
|
||||
class MyPdb:
|
||||
def __init__(self, *args, **kwargs):
|
||||
instances.append(self)
|
||||
|
||||
def set_trace(self, *args):
|
||||
print("set_trace_called", args)
|
||||
""",
|
||||
)
|
||||
result = testdir.runpytest(str(p1), "--pdbcls=mypdb:MyPdb", syspathinsert=True)
|
||||
assert result.ret == 0
|
||||
result.stdout.fnmatch_lines(
|
||||
["*set_trace_called*", "*set_trace_called*", "* 1 passed in *"]
|
||||
)
|
||||
|
|
Loading…
Reference in New Issue