Allow passing a custom Pdb subclass via --pdbcls.

This obviates the need for plugins such as `pytest-ipdb`; instead one
can simply call `py.test --pdb=IPython.core.debugger:Pdb`
This commit is contained in:
Antony Lee 2016-07-09 20:55:43 -07:00
parent 7a2058e3db
commit 6383b53ad9
3 changed files with 21 additions and 7 deletions

View File

@ -10,6 +10,7 @@ Anatoly Bubenkoff
Andreas Zeidler Andreas Zeidler
Andy Freeland Andy Freeland
Anthon van der Neut Anthon van der Neut
Antony Lee
Armin Rigo Armin Rigo
Aron Curzon Aron Curzon
Aviv Palivoda Aviv Palivoda

View File

@ -185,7 +185,8 @@
Before, you only got exceptions later from ``argparse`` library, Before, you only got exceptions later from ``argparse`` library,
giving no clue about the actual reason for double-added options. giving no clue about the actual reason for double-added options.
* * Allow passing a custom debugger class (e.g. ``IPython.core.debugger:Pdb``
via ``--pdbcls``.
* *

View File

@ -8,21 +8,32 @@ import pytest
def pytest_addoption(parser): def pytest_addoption(parser):
group = parser.getgroup("general") group = parser.getgroup("general")
group._addoption('--pdb', group._addoption(
action="store_true", dest="usepdb", default=False, '--pdb', dest="usepdb", action="store_true",
help="start the interactive Python debugger on errors.") help="start the interactive Python debugger on errors.")
group._addoption(
'--pdbcls', dest="usepdb_cls", metavar="modulename:classname",
help="start a custom interactive Python debugger on errors.")
def pytest_namespace(): def pytest_namespace():
return {'set_trace': pytestPDB().set_trace} return {'set_trace': pytestPDB().set_trace}
def pytest_configure(config): def pytest_configure(config):
if config.getvalue("usepdb"): if config.getvalue("usepdb") or config.getvalue("usepdb_cls"):
config.pluginmanager.register(PdbInvoke(), 'pdbinvoke') config.pluginmanager.register(PdbInvoke(), 'pdbinvoke')
if config.getvalue("usepdb_cls"):
modname, classname = config.getvalue("usepdb_cls").split(":")
__import__(modname)
pdb_cls = getattr(sys.modules[modname], classname)
else:
pdb_cls = pdb.Pdb
pytestPDB._pdb_cls = pdb_cls
old = (pdb.set_trace, pytestPDB._pluginmanager) old = (pdb.set_trace, pytestPDB._pluginmanager)
def fin(): def fin():
pdb.set_trace, pytestPDB._pluginmanager = old pdb.set_trace, pytestPDB._pluginmanager = old
pytestPDB._config = None pytestPDB._config = None
pytestPDB._pdb_cls = pdb.Pdb
pdb.set_trace = pytest.set_trace pdb.set_trace = pytest.set_trace
pytestPDB._pluginmanager = config.pluginmanager pytestPDB._pluginmanager = config.pluginmanager
pytestPDB._config = config pytestPDB._config = config
@ -32,6 +43,7 @@ class pytestPDB:
""" Pseudo PDB that defers to the real pdb. """ """ Pseudo PDB that defers to the real pdb. """
_pluginmanager = None _pluginmanager = None
_config = None _config = None
_pdb_cls = pdb.Pdb
def set_trace(self): def set_trace(self):
""" invoke PDB set_trace debugging, dropping any IO capturing. """ """ invoke PDB set_trace debugging, dropping any IO capturing. """
@ -45,7 +57,7 @@ class pytestPDB:
tw.line() tw.line()
tw.sep(">", "PDB set_trace (IO-capturing turned off)") tw.sep(">", "PDB set_trace (IO-capturing turned off)")
self._pluginmanager.hook.pytest_enter_pdb(config=self._config) self._pluginmanager.hook.pytest_enter_pdb(config=self._config)
pdb.Pdb().set_trace(frame) self._pdb_cls().set_trace(frame)
class PdbInvoke: class PdbInvoke:
@ -98,7 +110,7 @@ def _find_last_non_hidden_frame(stack):
def post_mortem(t): def post_mortem(t):
class Pdb(pdb.Pdb): class Pdb(pytestPDB._pdb_cls):
def get_stack(self, f, t): def get_stack(self, f, t):
stack, i = pdb.Pdb.get_stack(self, f, t) stack, i = pdb.Pdb.get_stack(self, f, t)
if f is None: if f is None: