Skip to content

Commit c4f20a1

Browse files
Merge pull request #1712 from anntzer/custom-debugger
Allow passing a custom Pdb subclass via --pdbcls.
2 parents 4c56c95 + 7ee3dd1 commit c4f20a1

File tree

4 files changed

+48
-7
lines changed

4 files changed

+48
-7
lines changed

AUTHORS

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ Anatoly Bubenkoff
1010
Andreas Zeidler
1111
Andy Freeland
1212
Anthon van der Neut
13+
Antony Lee
1314
Armin Rigo
1415
Aron Curzon
1516
Aviv Palivoda

CHANGELOG.rst

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -117,7 +117,8 @@
117117
Example '-o xfail_strict=True'. A complete ini-options can be viewed
118118
by py.test --help. Thanks `@blueyed`_ and `@fengxx`_ for the PR
119119

120-
*
120+
* Allow passing a custom debugger class (e.g. ``IPython.core.debugger:Pdb``
121+
via ``--pdbcls``). Thanks to `@anntzer`_ for the PR.
121122

122123
*
123124

@@ -270,6 +271,7 @@
270271
.. _@DRMacIver: https://github.com/DRMacIver
271272
.. _@RedBeardCode: https://github.com/RedBeardCode
272273
.. _@Vogtinator: https://github.com/Vogtinator
274+
.. _@anntzer: https://github.com/anntzer
273275
.. _@bagerard: https://github.com/bagerard
274276
.. _@blueyed: https://github.com/blueyed
275277
.. _@ceridwen: https://github.com/ceridwen

_pytest/debugging.py

Lines changed: 19 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,21 +8,33 @@
88

99
def pytest_addoption(parser):
1010
group = parser.getgroup("general")
11-
group._addoption('--pdb',
12-
action="store_true", dest="usepdb", default=False,
13-
help="start the interactive Python debugger on errors.")
11+
group._addoption(
12+
'--pdb', dest="usepdb", action="store_true",
13+
help="start the interactive Python debugger on errors.")
14+
group._addoption(
15+
'--pdbcls', dest="usepdb_cls", metavar="modulename:classname",
16+
help="start a custom interactive Python debugger on errors. "
17+
"For example: --pdbcls=IPython.core.debugger:Pdb")
1418

1519
def pytest_namespace():
1620
return {'set_trace': pytestPDB().set_trace}
1721

1822
def pytest_configure(config):
19-
if config.getvalue("usepdb"):
23+
if config.getvalue("usepdb") or config.getvalue("usepdb_cls"):
2024
config.pluginmanager.register(PdbInvoke(), 'pdbinvoke')
25+
if config.getvalue("usepdb_cls"):
26+
modname, classname = config.getvalue("usepdb_cls").split(":")
27+
__import__(modname)
28+
pdb_cls = getattr(sys.modules[modname], classname)
29+
else:
30+
pdb_cls = pdb.Pdb
31+
pytestPDB._pdb_cls = pdb_cls
2132

2233
old = (pdb.set_trace, pytestPDB._pluginmanager)
2334
def fin():
2435
pdb.set_trace, pytestPDB._pluginmanager = old
2536
pytestPDB._config = None
37+
pytestPDB._pdb_cls = pdb.Pdb
2638
pdb.set_trace = pytest.set_trace
2739
pytestPDB._pluginmanager = config.pluginmanager
2840
pytestPDB._config = config
@@ -32,6 +44,7 @@ class pytestPDB:
3244
""" Pseudo PDB that defers to the real pdb. """
3345
_pluginmanager = None
3446
_config = None
47+
_pdb_cls = pdb.Pdb
3548

3649
def set_trace(self):
3750
""" invoke PDB set_trace debugging, dropping any IO capturing. """
@@ -45,7 +58,7 @@ def set_trace(self):
4558
tw.line()
4659
tw.sep(">", "PDB set_trace (IO-capturing turned off)")
4760
self._pluginmanager.hook.pytest_enter_pdb(config=self._config)
48-
pdb.Pdb().set_trace(frame)
61+
self._pdb_cls().set_trace(frame)
4962

5063

5164
class PdbInvoke:
@@ -98,7 +111,7 @@ def _find_last_non_hidden_frame(stack):
98111

99112

100113
def post_mortem(t):
101-
class Pdb(pdb.Pdb):
114+
class Pdb(pytestPDB._pdb_cls):
102115
def get_stack(self, f, t):
103116
stack, i = pdb.Pdb.get_stack(self, f, t)
104117
if f is None:

testing/test_pdb.py

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -314,3 +314,28 @@ def test_foo():
314314
child.sendeof()
315315
if child.isalive():
316316
child.wait()
317+
318+
def test_pdb_custom_cls(self, testdir):
319+
called = []
320+
321+
# install dummy debugger class and track which methods were called on it
322+
class _CustomPdb:
323+
def __init__(self, *args, **kwargs):
324+
called.append("init")
325+
326+
def reset(self):
327+
called.append("reset")
328+
329+
def interaction(self, *args):
330+
called.append("interaction")
331+
332+
_pytest._CustomPdb = _CustomPdb
333+
334+
p1 = testdir.makepyfile("""xxx """)
335+
result = testdir.runpytest_inprocess(
336+
"--pdbcls=_pytest:_CustomPdb", p1)
337+
result.stdout.fnmatch_lines([
338+
"*NameError*xxx*",
339+
"*1 error*",
340+
])
341+
assert called == ["init", "reset", "interaction"]

0 commit comments

Comments
 (0)