diff --git a/changelog/4908.bugfix.rst b/changelog/4908.bugfix.rst new file mode 100644 index 000000000..2513618a1 --- /dev/null +++ b/changelog/4908.bugfix.rst @@ -0,0 +1 @@ +The ``pytest_enter_pdb`` hook gets called with post-mortem (``--pdb``). diff --git a/src/_pytest/capture.py b/src/_pytest/capture.py index ac2b5d4bd..68c17772f 100644 --- a/src/_pytest/capture.py +++ b/src/_pytest/capture.py @@ -359,8 +359,7 @@ class CaptureFixture(object): self._captured_err = self.captureclass.EMPTY_BUFFER def _start(self): - # Start if not started yet - if getattr(self, "_capture", None) is None: + if self._capture is None: self._capture = MultiCapture( out=True, err=True, in_=False, Capture=self.captureclass ) @@ -390,11 +389,13 @@ class CaptureFixture(object): def _suspend(self): """Suspends this fixture's own capturing temporarily.""" - self._capture.suspend_capturing() + if self._capture is not None: + self._capture.suspend_capturing() def _resume(self): """Resumes this fixture's own capturing temporarily.""" - self._capture.resume_capturing() + if self._capture is not None: + self._capture.resume_capturing() @contextlib.contextmanager def disabled(self): diff --git a/src/_pytest/debugging.py b/src/_pytest/debugging.py index 139645b17..3605490e6 100644 --- a/src/_pytest/debugging.py +++ b/src/_pytest/debugging.py @@ -213,6 +213,17 @@ class pytestPDB(object): self._pytest_capman.suspend_global_capture(in_=True) return ret + def get_stack(self, f, t): + stack, i = super(PytestPdbWrapper, self).get_stack(f, t) + if f is None: + # Find last non-hidden frame. + i = max(0, len(stack) - 1) + while i and stack[i][0].f_locals.get( + "__tracebackhide__", False + ): + i -= 1 + return stack, i + _pdb = PytestPdbWrapper(**kwargs) cls._pluginmanager.hook.pytest_enter_pdb(config=cls._config, pdb=_pdb) else: @@ -299,22 +310,8 @@ def _postmortem_traceback(excinfo): return excinfo._excinfo[2] -def _find_last_non_hidden_frame(stack): - i = max(0, len(stack) - 1) - while i and stack[i][0].f_locals.get("__tracebackhide__", False): - i -= 1 - return i - - def post_mortem(t): - class Pdb(pytestPDB._pdb_cls, object): - def get_stack(self, f, t): - stack, i = super(Pdb, self).get_stack(f, t) - if f is None: - i = _find_last_non_hidden_frame(stack) - return stack, i - - p = Pdb() + p = pytestPDB._init_pdb() p.reset() p.interaction(None, t) if p.quitting: diff --git a/testing/test_pdb.py b/testing/test_pdb.py index ed38a6133..4b084a26a 100644 --- a/testing/test_pdb.py +++ b/testing/test_pdb.py @@ -745,7 +745,8 @@ class TestPDB(object): ["E NameError: *xxx*", "*! *Exit: Quitting debugger !*"] # due to EOF ) - def test_enter_leave_pdb_hooks_are_called(self, testdir): + @pytest.mark.parametrize("post_mortem", (False, True)) + def test_enter_leave_pdb_hooks_are_called(self, post_mortem, testdir): testdir.makeconftest( """ mypdb = None @@ -774,16 +775,25 @@ class TestPDB(object): """ import pytest - def test_foo(): + def test_set_trace(): pytest.set_trace() assert 0 + + def test_post_mortem(): + assert 0 """ ) - child = testdir.spawn_pytest(str(p1)) + if post_mortem: + child = testdir.spawn_pytest(str(p1) + " --pdb -s -k test_post_mortem") + else: + child = testdir.spawn_pytest(str(p1) + " -k test_set_trace") child.expect("enter_pdb_hook") child.sendline("c") - child.expect(r"PDB continue \(IO-capturing resumed\)") - child.expect("Captured stdout call") + if post_mortem: + child.expect(r"PDB continue") + else: + child.expect(r"PDB continue \(IO-capturing resumed\)") + child.expect("Captured stdout call") rest = child.read().decode("utf8") assert "leave_pdb_hook" in rest assert "1 failed" in rest