From 92a2884b09eb793a390460012e6e2859da87767a Mon Sep 17 00:00:00 2001 From: Daniel Hahler Date: Sun, 18 Nov 2018 20:18:42 +0100 Subject: [PATCH] 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 https://github.com/pytest-dev/pytest/issues/4416. --- changelog/4416.feature.rst | 6 ++++++ src/_pytest/debugging.py | 28 ++++++++++++++++++---------- testing/test_pdb.py | 31 ++++++++++++++++++++++++++++++- 3 files changed, 54 insertions(+), 11 deletions(-) create mode 100644 changelog/4416.feature.rst diff --git a/changelog/4416.feature.rst b/changelog/4416.feature.rst new file mode 100644 index 000000000..89c0a84b1 --- /dev/null +++ b/changelog/4416.feature.rst @@ -0,0 +1,6 @@ +pdb: support keyword arguments with ``pdb.set_trace`` + +It 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.*"])``. diff --git a/src/_pytest/debugging.py b/src/_pytest/debugging.py index fe54d4939..adf9d0e54 100644 --- a/src/_pytest/debugging.py +++ b/src/_pytest/debugging.py @@ -77,18 +77,21 @@ class pytestPDB(object): _saved = [] @classmethod - def set_trace(cls, set_break=True): - """ invoke PDB set_trace debugging, dropping any IO capturing. """ + def _init_pdb(cls, *args, **kwargs): + """ Initialize PDB debugging, dropping any IO capturing. """ import _pytest.config - frame = sys._getframe().f_back if cls._pluginmanager is not None: capman = cls._pluginmanager.getplugin("capturemanager") if capman: capman.suspend_global_capture(in_=True) tw = _pytest.config.create_terminal_writer(cls._config) tw.line() - if capman and capman.is_globally_capturing(): + # Handle header similar to pdb.set_trace in py37+. + header = kwargs.pop("header", None) + if header is not None: + tw.sep(">", header) + elif capman and capman.is_globally_capturing(): tw.sep(">", "PDB set_trace (IO-capturing turned off)") else: tw.sep(">", "PDB set_trace") @@ -129,13 +132,18 @@ class pytestPDB(object): self._pytest_capman.suspend_global_capture(in_=True) return ret - _pdb = _PdbWrapper() + _pdb = _PdbWrapper(**kwargs) cls._pluginmanager.hook.pytest_enter_pdb(config=cls._config, pdb=_pdb) else: - _pdb = cls._pdb_cls() + _pdb = cls._pdb_cls(**kwargs) + return _pdb - if set_break: - _pdb.set_trace(frame) + @classmethod + def set_trace(cls, *args, **kwargs): + """Invoke debugging via ``Pdb.set_trace``, dropping any IO capturing.""" + frame = sys._getframe().f_back + _pdb = cls._init_pdb(*args, **kwargs) + _pdb.set_trace(frame) class PdbInvoke(object): @@ -161,9 +169,9 @@ class PdbTrace(object): def _test_pytest_function(pyfuncitem): - pytestPDB.set_trace(set_break=False) + _pdb = pytestPDB._init_pdb() testfunction = pyfuncitem.obj - pyfuncitem.obj = pdb.runcall + pyfuncitem.obj = _pdb.runcall if pyfuncitem._isyieldedfunction(): arg_list = list(pyfuncitem._args) arg_list.insert(0, testfunction) diff --git a/testing/test_pdb.py b/testing/test_pdb.py index dd349454b..41b4badda 100644 --- a/testing/test_pdb.py +++ b/testing/test_pdb.py @@ -390,6 +390,28 @@ class TestPDB(object): assert "hello17" in rest # out is captured self.flush(child) + def test_pdb_set_trace_kwargs(self, testdir): + p1 = testdir.makepyfile( + """ + import pytest + def test_1(): + i = 0 + print("hello17") + pytest.set_trace(header="== my_header ==") + x = 3 + """ + ) + child = testdir.spawn_pytest(str(p1)) + child.expect("== my_header ==") + assert "PDB set_trace" not in child.before.decode() + child.expect("Pdb") + child.sendeof() + rest = child.read().decode("utf-8") + assert "1 failed" in rest + assert "def test_1" in rest + assert "hello17" in rest # out is captured + self.flush(child) + def test_pdb_set_trace_interception(self, testdir): p1 = testdir.makepyfile( """ @@ -634,6 +656,12 @@ class TestPDB(object): testdir.makepyfile( custom_pdb=""" class CustomPdb(object): + def __init__(self, *args, **kwargs): + skip = kwargs.pop("skip") + assert skip == ["foo.*"] + print("__init__") + super(CustomPdb, self).__init__(*args, **kwargs) + def set_trace(*args, **kwargs): print('custom set_trace>') """ @@ -643,12 +671,13 @@ class TestPDB(object): import pytest def test_foo(): - pytest.set_trace() + pytest.set_trace(skip=['foo.*']) """ ) monkeypatch.setenv("PYTHONPATH", str(testdir.tmpdir)) child = testdir.spawn_pytest("--pdbcls=custom_pdb:CustomPdb %s" % str(p1)) + child.expect("__init__") child.expect("custom set_trace>") self.flush(child)