Merge pull request #3647 from jeffreyrack/3610-add-trace-option

3610 add trace option
This commit is contained in:
Bruno Oliveira 2018-07-07 10:05:34 -03:00 committed by GitHub
commit 303133f013
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 87 additions and 2 deletions

View File

@ -0,0 +1 @@
Added the `--trace` option to enter the debugger at the start of a test.

View File

@ -171,6 +171,18 @@ for example::
>>> sys.last_value >>> sys.last_value
AssertionError('assert result == "ok"',) AssertionError('assert result == "ok"',)
.. _trace-option:
Dropping to PDB_ (Python Debugger) at the start of a test
----------------------------------------------------------
``pytest`` allows one to drop into the PDB_ prompt immediately at the start of each test via a command line option::
pytest --trace
This will invoke the Python debugger at the start of every test.
.. _breakpoints: .. _breakpoints:
Setting breakpoints Setting breakpoints

View File

@ -5,6 +5,8 @@ import sys
import os import os
from doctest import UnexpectedException from doctest import UnexpectedException
from _pytest.config import hookimpl
try: try:
from builtins import breakpoint # noqa from builtins import breakpoint # noqa
@ -28,6 +30,12 @@ def pytest_addoption(parser):
help="start a custom interactive Python debugger on errors. " help="start a custom interactive Python debugger on errors. "
"For example: --pdbcls=IPython.terminal.debugger:TerminalPdb", "For example: --pdbcls=IPython.terminal.debugger:TerminalPdb",
) )
group._addoption(
"--trace",
dest="trace",
action="store_true",
help="Immediately break when running each test.",
)
def pytest_configure(config): def pytest_configure(config):
@ -38,6 +46,8 @@ def pytest_configure(config):
else: else:
pdb_cls = pdb.Pdb pdb_cls = pdb.Pdb
if config.getvalue("trace"):
config.pluginmanager.register(PdbTrace(), "pdbtrace")
if config.getvalue("usepdb"): if config.getvalue("usepdb"):
config.pluginmanager.register(PdbInvoke(), "pdbinvoke") config.pluginmanager.register(PdbInvoke(), "pdbinvoke")
@ -71,7 +81,7 @@ class pytestPDB(object):
_pdb_cls = pdb.Pdb _pdb_cls = pdb.Pdb
@classmethod @classmethod
def set_trace(cls): def set_trace(cls, set_break=True):
""" invoke PDB set_trace debugging, dropping any IO capturing. """ """ invoke PDB set_trace debugging, dropping any IO capturing. """
import _pytest.config import _pytest.config
@ -84,7 +94,8 @@ class pytestPDB(object):
tw.line() tw.line()
tw.sep(">", "PDB set_trace (IO-capturing turned off)") tw.sep(">", "PDB set_trace (IO-capturing turned off)")
cls._pluginmanager.hook.pytest_enter_pdb(config=cls._config) cls._pluginmanager.hook.pytest_enter_pdb(config=cls._config)
cls._pdb_cls().set_trace(frame) if set_break:
cls._pdb_cls().set_trace(frame)
class PdbInvoke(object): class PdbInvoke(object):
@ -104,6 +115,30 @@ class PdbInvoke(object):
post_mortem(tb) post_mortem(tb)
class PdbTrace(object):
@hookimpl(hookwrapper=True)
def pytest_pyfunc_call(self, pyfuncitem):
_test_pytest_function(pyfuncitem)
yield
def _test_pytest_function(pyfuncitem):
pytestPDB.set_trace(set_break=False)
testfunction = pyfuncitem.obj
pyfuncitem.obj = pdb.runcall
if pyfuncitem._isyieldedfunction():
arg_list = list(pyfuncitem._args)
arg_list.insert(0, testfunction)
pyfuncitem._args = tuple(arg_list)
else:
if "func" in pyfuncitem._fixtureinfo.argnames:
raise ValueError("--trace can't be used with a fixture named func!")
pyfuncitem.funcargs["func"] = testfunction
new_list = list(pyfuncitem._fixtureinfo.argnames)
new_list.append("func")
pyfuncitem._fixtureinfo.argnames = tuple(new_list)
def _enter_pdb(node, excinfo, rep): def _enter_pdb(node, excinfo, rep):
# XXX we re-use the TerminalReporter's terminalwriter # XXX we re-use the TerminalReporter's terminalwriter
# because this seems to avoid some encoding related troubles # because this seems to avoid some encoding related troubles

View File

@ -696,3 +696,40 @@ class TestDebuggingBreakpoints(object):
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
TestPDB.flush(child) TestPDB.flush(child)
class TestTraceOption:
def test_trace_sets_breakpoint(self, testdir):
p1 = testdir.makepyfile(
"""
def test_1():
assert True
"""
)
child = testdir.spawn_pytest("--trace " + str(p1))
child.expect("test_1")
child.expect("(Pdb)")
child.sendeof()
rest = child.read().decode("utf8")
assert "1 passed" in rest
assert "reading from stdin while output" not in rest
TestPDB.flush(child)
def test_trace_against_yield_test(self, testdir):
p1 = testdir.makepyfile(
"""
def is_equal(a, b):
assert a == b
def test_1():
yield is_equal, 1, 1
"""
)
child = testdir.spawn_pytest("--trace " + str(p1))
child.expect("is_equal")
child.expect("(Pdb)")
child.sendeof()
rest = child.read().decode("utf8")
assert "1 passed" in rest
assert "reading from stdin while output" not in rest
TestPDB.flush(child)