Fix --trace for parametrized tests

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>
Co-Authored-By: Daniel Hahler <git@thequod.de>
This commit is contained in:
David Szotten 2019-10-28 21:48:51 +00:00
parent cefe6bfec3
commit 285524c6cd
3 changed files with 57 additions and 8 deletions

View File

@ -0,0 +1 @@
Fix ``--trace`` when used with parametrized functions.

View File

@ -1,5 +1,6 @@
""" interactive debugging with PDB, the Python Debugger. """ """ interactive debugging with PDB, the Python Debugger. """
import argparse import argparse
import functools
import pdb import pdb
import sys import sys
from doctest import UnexpectedException from doctest import UnexpectedException
@ -274,13 +275,16 @@ class PdbTrace:
def _test_pytest_function(pyfuncitem): def _test_pytest_function(pyfuncitem):
_pdb = pytestPDB._init_pdb("runcall") _pdb = pytestPDB._init_pdb("runcall")
testfunction = pyfuncitem.obj testfunction = pyfuncitem.obj
pyfuncitem.obj = _pdb.runcall
if "func" in pyfuncitem._fixtureinfo.argnames: # pragma: no branch # we can't just return `partial(pdb.runcall, testfunction)` because (on
raise ValueError("--trace can't be used with a fixture named func!") # python < 3.7.4) runcall's first param is `func`, which means we'd get
pyfuncitem.funcargs["func"] = testfunction # an exception if one of the kwargs to testfunction was called `func`
new_list = list(pyfuncitem._fixtureinfo.argnames) @functools.wraps(testfunction)
new_list.append("func") def wrapper(*args, **kwargs):
pyfuncitem._fixtureinfo.argnames = tuple(new_list) func = functools.partial(testfunction, *args, **kwargs)
_pdb.runcall(func)
pyfuncitem.obj = wrapper
def _enter_pdb(node, excinfo, rep): def _enter_pdb(node, excinfo, rep):

View File

@ -1025,6 +1025,51 @@ class TestTraceOption:
assert "Exit: Quitting debugger" not in child.before.decode("utf8") assert "Exit: Quitting debugger" not in child.before.decode("utf8")
TestPDB.flush(child) 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): def test_trace_after_runpytest(testdir):
"""Test that debugging's pytest_configure is re-entrant.""" """Test that debugging's pytest_configure is re-entrant."""
@ -1150,7 +1195,6 @@ def test_pdbcls_via_local_module(testdir):
def runcall(self, *args, **kwds): def runcall(self, *args, **kwds):
print("runcall_called", args, kwds) print("runcall_called", args, kwds)
assert "func" in kwds
""", """,
) )
result = testdir.runpytest( result = testdir.runpytest(