Skip to content

Commit 92a2884

Browse files
committed
pdb: support kwargs with pdb.set_trace
This handles `header` similar to Python 3.7 does it, and forwards any other keyword arguments to the Pdb constructor. This allows for `__import__("pdb").set_trace(skip=["foo.*"])`. Fixes #4416.
1 parent 62967b3 commit 92a2884

File tree

3 files changed

+54
-11
lines changed

3 files changed

+54
-11
lines changed

changelog/4416.feature.rst

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
pdb: support keyword arguments with ``pdb.set_trace``
2+
3+
It handles ``header`` similar to Python 3.7 does it, and forwards any
4+
other keyword arguments to the ``Pdb`` constructor.
5+
6+
This allows for ``__import__("pdb").set_trace(skip=["foo.*"])``.

src/_pytest/debugging.py

Lines changed: 18 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -77,18 +77,21 @@ class pytestPDB(object):
7777
_saved = []
7878

7979
@classmethod
80-
def set_trace(cls, set_break=True):
81-
""" invoke PDB set_trace debugging, dropping any IO capturing. """
80+
def _init_pdb(cls, *args, **kwargs):
81+
""" Initialize PDB debugging, dropping any IO capturing. """
8282
import _pytest.config
8383

84-
frame = sys._getframe().f_back
8584
if cls._pluginmanager is not None:
8685
capman = cls._pluginmanager.getplugin("capturemanager")
8786
if capman:
8887
capman.suspend_global_capture(in_=True)
8988
tw = _pytest.config.create_terminal_writer(cls._config)
9089
tw.line()
91-
if capman and capman.is_globally_capturing():
90+
# Handle header similar to pdb.set_trace in py37+.
91+
header = kwargs.pop("header", None)
92+
if header is not None:
93+
tw.sep(">", header)
94+
elif capman and capman.is_globally_capturing():
9295
tw.sep(">", "PDB set_trace (IO-capturing turned off)")
9396
else:
9497
tw.sep(">", "PDB set_trace")
@@ -129,13 +132,18 @@ def setup(self, f, tb):
129132
self._pytest_capman.suspend_global_capture(in_=True)
130133
return ret
131134

132-
_pdb = _PdbWrapper()
135+
_pdb = _PdbWrapper(**kwargs)
133136
cls._pluginmanager.hook.pytest_enter_pdb(config=cls._config, pdb=_pdb)
134137
else:
135-
_pdb = cls._pdb_cls()
138+
_pdb = cls._pdb_cls(**kwargs)
139+
return _pdb
136140

137-
if set_break:
138-
_pdb.set_trace(frame)
141+
@classmethod
142+
def set_trace(cls, *args, **kwargs):
143+
"""Invoke debugging via ``Pdb.set_trace``, dropping any IO capturing."""
144+
frame = sys._getframe().f_back
145+
_pdb = cls._init_pdb(*args, **kwargs)
146+
_pdb.set_trace(frame)
139147

140148

141149
class PdbInvoke(object):
@@ -161,9 +169,9 @@ def pytest_pyfunc_call(self, pyfuncitem):
161169

162170

163171
def _test_pytest_function(pyfuncitem):
164-
pytestPDB.set_trace(set_break=False)
172+
_pdb = pytestPDB._init_pdb()
165173
testfunction = pyfuncitem.obj
166-
pyfuncitem.obj = pdb.runcall
174+
pyfuncitem.obj = _pdb.runcall
167175
if pyfuncitem._isyieldedfunction():
168176
arg_list = list(pyfuncitem._args)
169177
arg_list.insert(0, testfunction)

testing/test_pdb.py

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -390,6 +390,28 @@ def test_1():
390390
assert "hello17" in rest # out is captured
391391
self.flush(child)
392392

393+
def test_pdb_set_trace_kwargs(self, testdir):
394+
p1 = testdir.makepyfile(
395+
"""
396+
import pytest
397+
def test_1():
398+
i = 0
399+
print("hello17")
400+
pytest.set_trace(header="== my_header ==")
401+
x = 3
402+
"""
403+
)
404+
child = testdir.spawn_pytest(str(p1))
405+
child.expect("== my_header ==")
406+
assert "PDB set_trace" not in child.before.decode()
407+
child.expect("Pdb")
408+
child.sendeof()
409+
rest = child.read().decode("utf-8")
410+
assert "1 failed" in rest
411+
assert "def test_1" in rest
412+
assert "hello17" in rest # out is captured
413+
self.flush(child)
414+
393415
def test_pdb_set_trace_interception(self, testdir):
394416
p1 = testdir.makepyfile(
395417
"""
@@ -634,6 +656,12 @@ def test_pdb_custom_cls_with_settrace(self, testdir, monkeypatch):
634656
testdir.makepyfile(
635657
custom_pdb="""
636658
class CustomPdb(object):
659+
def __init__(self, *args, **kwargs):
660+
skip = kwargs.pop("skip")
661+
assert skip == ["foo.*"]
662+
print("__init__")
663+
super(CustomPdb, self).__init__(*args, **kwargs)
664+
637665
def set_trace(*args, **kwargs):
638666
print('custom set_trace>')
639667
"""
@@ -643,12 +671,13 @@ def set_trace(*args, **kwargs):
643671
import pytest
644672
645673
def test_foo():
646-
pytest.set_trace()
674+
pytest.set_trace(skip=['foo.*'])
647675
"""
648676
)
649677
monkeypatch.setenv("PYTHONPATH", str(testdir.tmpdir))
650678
child = testdir.spawn_pytest("--pdbcls=custom_pdb:CustomPdb %s" % str(p1))
651679

680+
child.expect("__init__")
652681
child.expect("custom set_trace>")
653682
self.flush(child)
654683

0 commit comments

Comments
 (0)