pdb: import pdbcls lazily

Fixes https://github.com/pytest-dev/pytest/issues/2064.
This commit is contained in:
Daniel Hahler 2019-05-24 18:10:05 +02:00
parent e393a73890
commit f2ed796c41
3 changed files with 38 additions and 32 deletions

View File

@ -0,0 +1 @@
The debugging plugin imports the wrapped ``Pdb`` class (``--pdbcls``) on-demand now.

View File

@ -49,42 +49,18 @@ def pytest_addoption(parser):
) )
def _import_pdbcls(modname, classname):
try:
__import__(modname)
mod = sys.modules[modname]
# Handle --pdbcls=pdb:pdb.Pdb (useful e.g. with pdbpp).
parts = classname.split(".")
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))
def pytest_configure(config): def pytest_configure(config):
pdb_cls = config.getvalue("usepdb_cls")
if pdb_cls:
pdb_cls = _import_pdbcls(*pdb_cls)
else:
pdb_cls = pdb.Pdb
if config.getvalue("trace"): if config.getvalue("trace"):
config.pluginmanager.register(PdbTrace(), "pdbtrace") config.pluginmanager.register(PdbTrace(), "pdbtrace")
if config.getvalue("usepdb"): if config.getvalue("usepdb"):
config.pluginmanager.register(PdbInvoke(), "pdbinvoke") config.pluginmanager.register(PdbInvoke(), "pdbinvoke")
pytestPDB._saved.append( pytestPDB._saved.append(
(pdb.set_trace, pytestPDB._pluginmanager, pytestPDB._config, pytestPDB._pdb_cls) (pdb.set_trace, pytestPDB._pluginmanager, pytestPDB._config)
) )
pdb.set_trace = pytestPDB.set_trace pdb.set_trace = pytestPDB.set_trace
pytestPDB._pluginmanager = config.pluginmanager pytestPDB._pluginmanager = config.pluginmanager
pytestPDB._config = config pytestPDB._config = config
pytestPDB._pdb_cls = pdb_cls
# NOTE: not using pytest_unconfigure, since it might get called although # NOTE: not using pytest_unconfigure, since it might get called although
# pytest_configure was not (if another plugin raises UsageError). # pytest_configure was not (if another plugin raises UsageError).
@ -93,7 +69,6 @@ def pytest_configure(config):
pdb.set_trace, pdb.set_trace,
pytestPDB._pluginmanager, pytestPDB._pluginmanager,
pytestPDB._config, pytestPDB._config,
pytestPDB._pdb_cls,
) = pytestPDB._saved.pop() ) = pytestPDB._saved.pop()
config._cleanup.append(fin) config._cleanup.append(fin)
@ -104,7 +79,6 @@ class pytestPDB(object):
_pluginmanager = None _pluginmanager = None
_config = None _config = None
_pdb_cls = pdb.Pdb
_saved = [] _saved = []
_recursive_debug = 0 _recursive_debug = 0
@ -114,6 +88,33 @@ class pytestPDB(object):
return capman.is_capturing() return capman.is_capturing()
return False return False
@classmethod
def _import_pdb_cls(cls):
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
modname, classname = pdb_cls
try:
__import__(modname)
mod = sys.modules[modname]
# Handle --pdbcls=pdb:pdb.Pdb (useful e.g. with pdbpp).
parts = classname.split(".")
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 @classmethod
def _init_pdb(cls, *args, **kwargs): def _init_pdb(cls, *args, **kwargs):
""" Initialize PDB debugging, dropping any IO capturing. """ """ Initialize PDB debugging, dropping any IO capturing. """
@ -144,7 +145,9 @@ class pytestPDB(object):
else: else:
tw.sep(">", "PDB set_trace") tw.sep(">", "PDB set_trace")
class PytestPdbWrapper(cls._pdb_cls, object): pdb_cls = cls._import_pdb_cls()
class PytestPdbWrapper(pdb_cls, object):
_pytest_capman = capman _pytest_capman = capman
_continued = False _continued = False
@ -227,7 +230,8 @@ class pytestPDB(object):
_pdb = PytestPdbWrapper(**kwargs) _pdb = PytestPdbWrapper(**kwargs)
cls._pluginmanager.hook.pytest_enter_pdb(config=cls._config, pdb=_pdb) cls._pluginmanager.hook.pytest_enter_pdb(config=cls._config, pdb=_pdb)
else: else:
_pdb = cls._pdb_cls(**kwargs) pdb_cls = cls._import_pdb_cls()
_pdb = pdb_cls(**kwargs)
return _pdb return _pdb
@classmethod @classmethod

View File

@ -1157,12 +1157,13 @@ def test_pdbcls_via_local_module(testdir):
result = testdir.runpytest( result = testdir.runpytest(
str(p1), "--pdbcls=really.invalid:Value", syspathinsert=True str(p1), "--pdbcls=really.invalid:Value", syspathinsert=True
) )
result.stderr.fnmatch_lines( result.stdout.fnmatch_lines(
[ [
"ERROR: --pdbcls: could not import 'really.invalid:Value': No module named *really*" "*= FAILURES =*",
"E * --pdbcls: could not import 'really.invalid:Value': No module named *really*",
] ]
) )
assert result.ret == 4 assert result.ret == 1
result = testdir.runpytest( result = testdir.runpytest(
str(p1), "--pdbcls=mypdb:Wrapped.MyPdb", syspathinsert=True str(p1), "--pdbcls=mypdb:Wrapped.MyPdb", syspathinsert=True