Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

pdb: trigger pytest_enter_pdb hook with post-mortem #4908

Merged
merged 2 commits into from May 23, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions changelog/4908.bugfix.rst
@@ -0,0 +1 @@
The ``pytest_enter_pdb`` hook gets called with post-mortem (``--pdb``).
9 changes: 5 additions & 4 deletions src/_pytest/capture.py
Expand Up @@ -358,8 +358,7 @@ def __init__(self, captureclass, request):
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
)
Expand Down Expand Up @@ -389,11 +388,13 @@ def readouterr(self):

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):
Expand Down
27 changes: 12 additions & 15 deletions src/_pytest/debugging.py
Expand Up @@ -212,6 +212,17 @@ def setup(self, f, tb):
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:
Expand Down Expand Up @@ -298,22 +309,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:
Expand Down
20 changes: 15 additions & 5 deletions testing/test_pdb.py
Expand Up @@ -744,7 +744,8 @@ def test_pdb_collection_failure_is_shown(self, testdir):
["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
Expand Down Expand Up @@ -773,16 +774,25 @@ def pytest_leave_pdb(config, pdb):
"""
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
Expand Down