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: improve quitting from debugger #4280

Merged
merged 1 commit into from
Jan 29, 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.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions changelog/4280.bugfix.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
Improve quitting from pdb, especially with ``--trace``.

Using ``q[quit]`` after ``pdb.set_trace()`` will quit pytest also.
10 changes: 7 additions & 3 deletions src/_pytest/debugging.py
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,10 @@ def do_continue(self, arg):

do_c = do_cont = do_continue

def set_quit(self):
super(_PdbWrapper, self).set_quit()
outcomes.exit("Quitting debugger")

def setup(self, f, tb):
"""Suspend on setup().

Expand Down Expand Up @@ -210,8 +214,7 @@ def _enter_pdb(node, excinfo, rep):
tw.sep(">", "entering PDB")
tb = _postmortem_traceback(excinfo)
rep._pdbshown = True
if post_mortem(tb):
outcomes.exit("Quitting debugger")
post_mortem(tb)
return rep


Expand Down Expand Up @@ -242,4 +245,5 @@ def get_stack(self, f, t):
p = Pdb()
p.reset()
p.interaction(None, t)
return p.quitting
if p.quitting:
outcomes.exit("Quitting debugger")
6 changes: 3 additions & 3 deletions src/_pytest/outcomes.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,21 +49,21 @@ class Failed(OutcomeException):
__module__ = "builtins"


class Exit(SystemExit):
class Exit(Exception):
""" raised for immediate program exits (no tracebacks/summaries)"""

def __init__(self, msg="unknown reason", returncode=None):
self.msg = msg
self.returncode = returncode
SystemExit.__init__(self, msg)
super(Exit, self).__init__(msg)


# exposed helper methods


def exit(msg, returncode=None):
"""
Exit testing process as if SystemExit was triggered.
Exit testing process.

:param str msg: message to display upon exit.
:param int returncode: return code to be used when exiting pytest.
Expand Down
126 changes: 71 additions & 55 deletions testing/test_pdb.py
Original file line number Diff line number Diff line change
Expand Up @@ -147,29 +147,6 @@ def test_func():
assert rep.failed
assert len(pdblist) == 1

def test_pdb_interaction(self, testdir):
p1 = testdir.makepyfile(
"""
def test_1():
i = 0
assert i == 1

def test_not_called_due_to_quit():
pass
"""
)
child = testdir.spawn_pytest("--pdb %s" % p1)
child.expect(".*def test_1")
child.expect(".*i = 0")
child.expect("Pdb")
child.sendeof()
rest = child.read().decode("utf8")
assert "= 1 failed in" in rest
assert "def test_1" not in rest
assert "Exit: Quitting debugger" in rest
assert "PDB continue (IO-capturing resumed)" not in rest
self.flush(child)

@staticmethod
def flush(child):
if platform.system() == "Darwin":
Expand Down Expand Up @@ -214,40 +191,32 @@ def test_one(self):
child.sendeof()
self.flush(child)

def test_pdb_print_captured_stdout(self, testdir):
def test_pdb_print_captured_stdout_and_stderr(self, testdir):
p1 = testdir.makepyfile(
"""
def test_1():
import sys
sys.stderr.write("get\\x20rekt")
print("get\\x20rekt")
assert False

def test_not_called_due_to_quit():
pass
"""
)
child = testdir.spawn_pytest("--pdb %s" % p1)
child.expect("captured stdout")
child.expect("get rekt")
child.expect("Pdb")
child.sendeof()
rest = child.read().decode("utf8")
assert "1 failed" in rest
assert "get rekt" not in rest
self.flush(child)

def test_pdb_print_captured_stderr(self, testdir):
p1 = testdir.makepyfile(
"""
def test_1():
import sys
sys.stderr.write("get\\x20rekt")
assert False
"""
)
child = testdir.spawn_pytest("--pdb %s" % p1)
child.expect("captured stderr")
child.expect("get rekt")
child.expect("traceback")
child.expect("def test_1")
child.expect("Pdb")
child.sendeof()
rest = child.read().decode("utf8")
assert "1 failed" in rest
assert "Exit: Quitting debugger" in rest
assert "= 1 failed in" in rest
assert "def test_1" not in rest
assert "get rekt" not in rest
self.flush(child)

Expand Down Expand Up @@ -375,15 +344,17 @@ def test_1():
i = 0
print("hello17")
pytest.set_trace()
x = 3
i == 1
assert 0
"""
)
child = testdir.spawn_pytest(str(p1))
child.expect("test_1")
child.expect("x = 3")
child.expect(r"test_1\(\)")
child.expect("i == 1")
child.expect("Pdb")
child.sendeof()
child.sendline("c")
rest = child.read().decode("utf-8")
assert "AssertionError" in rest
assert "1 failed" in rest
assert "def test_1" in rest
assert "hello17" in rest # out is captured
Expand All @@ -398,13 +369,14 @@ def test_1():
print("hello17")
pytest.set_trace(header="== my_header ==")
x = 3
assert 0
"""
)
child = testdir.spawn_pytest(str(p1))
child.expect("== my_header ==")
assert "PDB set_trace" not in child.before.decode()
child.expect("Pdb")
child.sendeof()
child.sendline("c")
rest = child.read().decode("utf-8")
assert "1 failed" in rest
assert "def test_1" in rest
Expand All @@ -424,9 +396,9 @@ def test_1():
child.expect("Pdb")
child.sendeof()
rest = child.read().decode("utf8")
assert "1 failed" in rest
assert "no tests ran" in rest
assert "reading from stdin while output" not in rest
assert "BdbQuit" in rest
assert "BdbQuit" not in rest
self.flush(child)

def test_pdb_and_capsys(self, testdir):
Expand Down Expand Up @@ -518,6 +490,7 @@ def test_1():
print("hello18")
pytest.set_trace()
x = 4
assert 0
"""
)
child = testdir.spawn_pytest(str(p1))
Expand All @@ -530,11 +503,11 @@ def test_1():
child.expect(r"PDB set_trace \(IO-capturing turned off\)")
child.expect("x = 4")
child.expect("Pdb")
child.sendeof()
child.sendline("c")
child.expect("_ test_1 _")
child.expect("def test_1")
child.expect("Captured stdout call")
rest = child.read().decode("utf8")
assert "Captured stdout call" in rest
assert "hello17" in rest # out is captured
assert "hello18" in rest # out is captured
assert "1 failed" in rest
Expand Down Expand Up @@ -795,7 +768,7 @@ def test_1():
child.expect("Pdb")
child.sendeof()
rest = child.read().decode("utf8")
assert "1 failed" in rest
assert "Quitting debugger" in rest
assert "reading from stdin while output" not in rest
TestPDB.flush(child)

Expand All @@ -808,12 +781,13 @@ def test_pdb_not_altered(self, testdir):
import pdb
def test_1():
pdb.set_trace()
assert 0
"""
)
child = testdir.spawn_pytest(str(p1))
child.expect("test_1")
child.expect("Pdb")
child.sendeof()
child.sendline("c")
rest = child.read().decode("utf8")
assert "1 failed" in rest
assert "reading from stdin while output" not in rest
Expand All @@ -826,15 +800,29 @@ def test_trace_sets_breakpoint(self, testdir):
"""
def test_1():
assert True

def test_2():
pass

def test_3():
pass
"""
)
child = testdir.spawn_pytest("--trace " + str(p1))
child.expect("test_1")
child.expect("Pdb")
child.sendeof()
child.sendline("c")
child.expect("test_2")
child.expect("Pdb")
child.sendline("c")
child.expect("test_3")
child.expect("Pdb")
child.sendline("q")
child.expect_exact("Exit: Quitting debugger")
rest = child.read().decode("utf8")
assert "1 passed" in rest
assert "2 passed in" in rest
assert "reading from stdin while output" not in rest
assert "Exit: Quitting debugger" in child.before.decode("utf8")
TestPDB.flush(child)


Expand Down Expand Up @@ -863,3 +851,31 @@ def test_inner(testdir):
rest = child.read().decode("utf8")
TestPDB.flush(child)
assert child.exitstatus == 0, rest


def test_quit_with_swallowed_SystemExit(testdir):
"""Test that debugging's pytest_configure is re-entrant."""
p1 = testdir.makepyfile(
"""
def call_pdb_set_trace():
__import__('pdb').set_trace()


def test_1():
try:
call_pdb_set_trace()
except SystemExit:
pass


def test_2():
pass
"""
)
child = testdir.spawn_pytest(str(p1))
child.expect("Pdb")
child.sendline("q")
child.expect_exact("Exit: Quitting debugger")
rest = child.read().decode("utf8")
assert "no tests ran" in rest
TestPDB.flush(child)