Skip to content

Commit

Permalink
Fix --trace for parametrized tests
Browse files Browse the repository at this point in the history
Without this, the second time it tries to stop in a parametrized
function it raises instead:

`ValueError: --trace can't be used with a fixture named func!`

Implementation idea, test (and changelog tweaks) thanks to @blueyed

Co-Authored-By: Ronny Pfannschmidt <opensource@ronnypfannschmidt.de>
  • Loading branch information
davidszotten and RonnyPfannschmidt committed Oct 30, 2019
1 parent cefe6bf commit 1463321
Show file tree
Hide file tree
Showing 3 changed files with 71 additions and 12 deletions.
1 change: 1 addition & 0 deletions changelog/6099.bugfix.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Fix ``--trace`` when used with parametrized functions.
27 changes: 20 additions & 7 deletions src/_pytest/debugging.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
""" interactive debugging with PDB, the Python Debugger. """
import argparse
import bdb
import functools
import pdb
import sys
from doctest import UnexpectedException
Expand Down Expand Up @@ -274,13 +276,24 @@ def pytest_pyfunc_call(self, pyfuncitem):
def _test_pytest_function(pyfuncitem):
_pdb = pytestPDB._init_pdb("runcall")
testfunction = pyfuncitem.obj
pyfuncitem.obj = _pdb.runcall
if "func" in pyfuncitem._fixtureinfo.argnames: # pragma: no branch
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)

# we need a copy of bdb.runcall because it takes `func` as a parameter
# which means we can't have a kwarg called func
@functools.wraps(testfunction)
def runcall(*args, **kwds):
_pdb.reset()
sys.settrace(_pdb.trace_dispatch)
res = None
try:
res = testfunction(*args, **kwds)
except bdb.BdbQuit:
pass
finally:
_pdb.quitting = True
sys.settrace(None)
return res

pyfuncitem.obj = runcall


def _enter_pdb(node, excinfo, rep):
Expand Down
55 changes: 50 additions & 5 deletions testing/test_pdb.py
Original file line number Diff line number Diff line change
Expand Up @@ -1025,6 +1025,51 @@ def test_3():
assert "Exit: Quitting debugger" not in child.before.decode("utf8")
TestPDB.flush(child)

def test_trace_with_parametrize_handles_shared_fixtureinfo(self, testdir):
p1 = testdir.makepyfile(
"""
import pytest
@pytest.mark.parametrize('myparam', [1,2])
def test_1(myparam, request):
assert myparam in (1, 2)
assert request.function.__name__ == "test_1"
@pytest.mark.parametrize('func', [1,2])
def test_func(func, request):
assert func in (1, 2)
assert request.function.__name__ == "test_func"
@pytest.mark.parametrize('myparam', [1,2])
def test_func_kw(myparam, request, func="func_kw"):
assert myparam in (1, 2)
assert func == "func_kw"
assert request.function.__name__ == "test_func_kw"
"""
)
child = testdir.spawn_pytest("--trace " + str(p1))
for func, argname in [
("test_1", "myparam"),
("test_func", "func"),
("test_func_kw", "myparam"),
]:
child.expect_exact("> PDB runcall (IO-capturing turned off) >")
child.expect_exact(func)
child.expect_exact("Pdb")
child.sendline("args")
child.expect_exact("{} = 1\r\n".format(argname))
child.expect_exact("Pdb")
child.sendline("c")
child.expect_exact("Pdb")
child.sendline("args")
child.expect_exact("{} = 2\r\n".format(argname))
child.expect_exact("Pdb")
child.sendline("c")
child.expect_exact("> PDB continue (IO-capturing resumed) >")
rest = child.read().decode("utf8")
assert "6 passed in" in rest
assert "reading from stdin while output" not in rest
# Only printed once - not on stderr.
assert "Exit: Quitting debugger" not in child.before.decode("utf8")
TestPDB.flush(child)


def test_trace_after_runpytest(testdir):
"""Test that debugging's pytest_configure is re-entrant."""
Expand Down Expand Up @@ -1143,14 +1188,14 @@ def test():
__import__("pdb").set_trace()
""",
mypdb="""
import pdb
class Wrapped:
class MyPdb:
class MyPdb(pdb.Pdb):
def set_trace(self, *args):
print("set_trace_called", args)
def runcall(self, *args, **kwds):
print("runcall_called", args, kwds)
assert "func" in kwds
def trace_dispatch(self, frame, event, arg):
print("trace_dispatch called", frame, event, arg)
""",
)
result = testdir.runpytest(
Expand All @@ -1175,7 +1220,7 @@ def runcall(self, *args, **kwds):
str(p1), "--pdbcls=mypdb:Wrapped.MyPdb", "--trace", syspathinsert=True
)
assert result.ret == 0
result.stdout.fnmatch_lines(["*runcall_called*", "* 1 passed in *"])
result.stdout.fnmatch_lines(["*trace_dispatch called*", "* 1 passed in *"])


def test_raises_bdbquit_with_eoferror(testdir):
Expand Down

0 comments on commit 1463321

Please sign in to comment.