Improve quitting from pdb

Regarding tests: it merges ``test_pdb_interaction``,
``test_pdb_print_captured_stdout``, and
``test_pdb_print_captured_stderr`` into
``test_pdb_print_captured_stdout_and_stderr`` (clarity and performance,
especially since pexpect tests are slow).
This commit is contained in:
Daniel Hahler 2018-10-31 16:20:44 +01:00
parent 4947eb85c0
commit e69b1255d7
4 changed files with 85 additions and 62 deletions

View File

@ -0,0 +1,3 @@
Improve quitting from pdb, especially with ``--trace``.
Using ``q[quit]`` after ``pdb.set_trace()`` will quit pytest also.

View File

@ -118,6 +118,10 @@ class pytestPDB(object):
do_c = do_cont = do_continue do_c = do_cont = do_continue
def set_quit(self):
super(_PdbWrapper, self).set_quit()
outcomes.exit("Quitting debugger")
def setup(self, f, tb): def setup(self, f, tb):
"""Suspend on setup(). """Suspend on setup().
@ -210,8 +214,7 @@ def _enter_pdb(node, excinfo, rep):
tw.sep(">", "entering PDB") tw.sep(">", "entering PDB")
tb = _postmortem_traceback(excinfo) tb = _postmortem_traceback(excinfo)
rep._pdbshown = True rep._pdbshown = True
if post_mortem(tb): post_mortem(tb)
outcomes.exit("Quitting debugger")
return rep return rep
@ -242,4 +245,5 @@ def post_mortem(t):
p = Pdb() p = Pdb()
p.reset() p.reset()
p.interaction(None, t) p.interaction(None, t)
return p.quitting if p.quitting:
outcomes.exit("Quitting debugger")

View File

@ -49,13 +49,13 @@ class Failed(OutcomeException):
__module__ = "builtins" __module__ = "builtins"
class Exit(SystemExit): class Exit(Exception):
""" raised for immediate program exits (no tracebacks/summaries)""" """ raised for immediate program exits (no tracebacks/summaries)"""
def __init__(self, msg="unknown reason", returncode=None): def __init__(self, msg="unknown reason", returncode=None):
self.msg = msg self.msg = msg
self.returncode = returncode self.returncode = returncode
SystemExit.__init__(self, msg) super(Exit, self).__init__(msg)
# exposed helper methods # exposed helper methods
@ -63,7 +63,7 @@ class Exit(SystemExit):
def exit(msg, returncode=None): 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 str msg: message to display upon exit.
:param int returncode: return code to be used when exiting pytest. :param int returncode: return code to be used when exiting pytest.

View File

@ -147,29 +147,6 @@ class TestPDB(object):
assert rep.failed assert rep.failed
assert len(pdblist) == 1 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 @staticmethod
def flush(child): def flush(child):
if platform.system() == "Darwin": if platform.system() == "Darwin":
@ -214,40 +191,32 @@ class TestPDB(object):
child.sendeof() child.sendeof()
self.flush(child) 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():
print("get\\x20rekt")
assert False
"""
)
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( p1 = testdir.makepyfile(
""" """
def test_1(): def test_1():
import sys import sys
sys.stderr.write("get\\x20rekt") sys.stderr.write("get\\x20rekt")
print("get\\x20rekt")
assert False assert False
def test_not_called_due_to_quit():
pass
""" """
) )
child = testdir.spawn_pytest("--pdb %s" % p1) child = testdir.spawn_pytest("--pdb %s" % p1)
child.expect("captured stdout")
child.expect("get rekt")
child.expect("captured stderr") child.expect("captured stderr")
child.expect("get rekt") child.expect("get rekt")
child.expect("traceback")
child.expect("def test_1")
child.expect("Pdb") child.expect("Pdb")
child.sendeof() child.sendeof()
rest = child.read().decode("utf8") 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 assert "get rekt" not in rest
self.flush(child) self.flush(child)
@ -375,15 +344,17 @@ class TestPDB(object):
i = 0 i = 0
print("hello17") print("hello17")
pytest.set_trace() pytest.set_trace()
x = 3 i == 1
assert 0
""" """
) )
child = testdir.spawn_pytest(str(p1)) child = testdir.spawn_pytest(str(p1))
child.expect("test_1") child.expect(r"test_1\(\)")
child.expect("x = 3") child.expect("i == 1")
child.expect("Pdb") child.expect("Pdb")
child.sendeof() child.sendline("c")
rest = child.read().decode("utf-8") rest = child.read().decode("utf-8")
assert "AssertionError" in rest
assert "1 failed" in rest assert "1 failed" in rest
assert "def test_1" in rest assert "def test_1" in rest
assert "hello17" in rest # out is captured assert "hello17" in rest # out is captured
@ -398,13 +369,14 @@ class TestPDB(object):
print("hello17") print("hello17")
pytest.set_trace(header="== my_header ==") pytest.set_trace(header="== my_header ==")
x = 3 x = 3
assert 0
""" """
) )
child = testdir.spawn_pytest(str(p1)) child = testdir.spawn_pytest(str(p1))
child.expect("== my_header ==") child.expect("== my_header ==")
assert "PDB set_trace" not in child.before.decode() assert "PDB set_trace" not in child.before.decode()
child.expect("Pdb") child.expect("Pdb")
child.sendeof() child.sendline("c")
rest = child.read().decode("utf-8") rest = child.read().decode("utf-8")
assert "1 failed" in rest assert "1 failed" in rest
assert "def test_1" in rest assert "def test_1" in rest
@ -424,9 +396,9 @@ class TestPDB(object):
child.expect("Pdb") child.expect("Pdb")
child.sendeof() child.sendeof()
rest = child.read().decode("utf8") 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 "reading from stdin while output" not in rest
assert "BdbQuit" in rest assert "BdbQuit" not in rest
self.flush(child) self.flush(child)
def test_pdb_and_capsys(self, testdir): def test_pdb_and_capsys(self, testdir):
@ -518,6 +490,7 @@ class TestPDB(object):
print("hello18") print("hello18")
pytest.set_trace() pytest.set_trace()
x = 4 x = 4
assert 0
""" """
) )
child = testdir.spawn_pytest(str(p1)) child = testdir.spawn_pytest(str(p1))
@ -530,11 +503,11 @@ class TestPDB(object):
child.expect(r"PDB set_trace \(IO-capturing turned off\)") child.expect(r"PDB set_trace \(IO-capturing turned off\)")
child.expect("x = 4") child.expect("x = 4")
child.expect("Pdb") child.expect("Pdb")
child.sendeof() child.sendline("c")
child.expect("_ test_1 _") child.expect("_ test_1 _")
child.expect("def test_1") child.expect("def test_1")
child.expect("Captured stdout call")
rest = child.read().decode("utf8") rest = child.read().decode("utf8")
assert "Captured stdout call" in rest
assert "hello17" in rest # out is captured assert "hello17" in rest # out is captured
assert "hello18" in rest # out is captured assert "hello18" in rest # out is captured
assert "1 failed" in rest assert "1 failed" in rest
@ -795,7 +768,7 @@ class TestDebuggingBreakpoints(object):
child.expect("Pdb") child.expect("Pdb")
child.sendeof() child.sendeof()
rest = child.read().decode("utf8") rest = child.read().decode("utf8")
assert "1 failed" in rest assert "Quitting debugger" in rest
assert "reading from stdin while output" not in rest assert "reading from stdin while output" not in rest
TestPDB.flush(child) TestPDB.flush(child)
@ -808,12 +781,13 @@ class TestDebuggingBreakpoints(object):
import pdb import pdb
def test_1(): def test_1():
pdb.set_trace() pdb.set_trace()
assert 0
""" """
) )
child = testdir.spawn_pytest(str(p1)) child = testdir.spawn_pytest(str(p1))
child.expect("test_1") child.expect("test_1")
child.expect("Pdb") child.expect("Pdb")
child.sendeof() child.sendline("c")
rest = child.read().decode("utf8") rest = child.read().decode("utf8")
assert "1 failed" in rest assert "1 failed" in rest
assert "reading from stdin while output" not in rest assert "reading from stdin while output" not in rest
@ -826,15 +800,29 @@ class TestTraceOption:
""" """
def test_1(): def test_1():
assert True assert True
def test_2():
pass
def test_3():
pass
""" """
) )
child = testdir.spawn_pytest("--trace " + str(p1)) child = testdir.spawn_pytest("--trace " + str(p1))
child.expect("test_1") child.expect("test_1")
child.expect("Pdb") 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") 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 "reading from stdin while output" not in rest
assert "Exit: Quitting debugger" in child.before.decode("utf8")
TestPDB.flush(child) TestPDB.flush(child)
@ -863,3 +851,31 @@ def test_trace_after_runpytest(testdir):
rest = child.read().decode("utf8") rest = child.read().decode("utf8")
TestPDB.flush(child) TestPDB.flush(child)
assert child.exitstatus == 0, rest 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)