Skip to content

Commit

Permalink
pdb: import pdbcls lazily
Browse files Browse the repository at this point in the history
  • Loading branch information
blueyed committed May 24, 2019
1 parent e393a73 commit c6e16ce
Show file tree
Hide file tree
Showing 2 changed files with 36 additions and 32 deletions.
61 changes: 32 additions & 29 deletions src/_pytest/debugging.py
Expand Up @@ -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):
pdb_cls = config.getvalue("usepdb_cls")
if pdb_cls:
pdb_cls = _import_pdbcls(*pdb_cls)
else:
pdb_cls = pdb.Pdb

if config.getvalue("trace"):
config.pluginmanager.register(PdbTrace(), "pdbtrace")
if config.getvalue("usepdb"):
config.pluginmanager.register(PdbInvoke(), "pdbinvoke")

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
pytestPDB._pluginmanager = config.pluginmanager
pytestPDB._config = config
pytestPDB._pdb_cls = pdb_cls

# NOTE: not using pytest_unconfigure, since it might get called although
# pytest_configure was not (if another plugin raises UsageError).
Expand All @@ -93,7 +69,6 @@ def fin():
pdb.set_trace,
pytestPDB._pluginmanager,
pytestPDB._config,
pytestPDB._pdb_cls,
) = pytestPDB._saved.pop()

config._cleanup.append(fin)
Expand All @@ -104,7 +79,6 @@ class pytestPDB(object):

_pluginmanager = None
_config = None
_pdb_cls = pdb.Pdb
_saved = []
_recursive_debug = 0

Expand All @@ -114,6 +88,32 @@ def _is_capturing(cls, capman):
return capman.is_capturing()
return False

@classmethod
def _import_pdb_cls(cls):
if cls._config:
pdb_cls = cls._config.getvalue("usepdb_cls")
if pdb_cls:
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)
)

return pdb.Pdb

@classmethod
def _init_pdb(cls, *args, **kwargs):
""" Initialize PDB debugging, dropping any IO capturing. """
Expand Down Expand Up @@ -144,7 +144,9 @@ def _init_pdb(cls, *args, **kwargs):
else:
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
_continued = False

Expand Down Expand Up @@ -227,7 +229,8 @@ def get_stack(self, f, t):
_pdb = PytestPdbWrapper(**kwargs)
cls._pluginmanager.hook.pytest_enter_pdb(config=cls._config, pdb=_pdb)
else:
_pdb = cls._pdb_cls(**kwargs)
pdb_cls = cls._import_pdb_cls()
_pdb = pdb_cls(**kwargs)
return _pdb

@classmethod
Expand Down
7 changes: 4 additions & 3 deletions testing/test_pdb.py
Expand Up @@ -1157,12 +1157,13 @@ def runcall(self, *args, **kwds):
result = testdir.runpytest(
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(
str(p1), "--pdbcls=mypdb:Wrapped.MyPdb", syspathinsert=True
Expand Down

0 comments on commit c6e16ce

Please sign in to comment.