From 285524c6cdde61fde3d9b4f250b4fa9bbb46e932 Mon Sep 17 00:00:00 2001 From: David Szotten Date: Mon, 28 Oct 2019 21:48:51 +0000 Subject: [PATCH] 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 Co-Authored-By: Daniel Hahler --- changelog/6099.bugfix.rst | 1 + src/_pytest/debugging.py | 18 +++++++++------ testing/test_pdb.py | 46 ++++++++++++++++++++++++++++++++++++++- 3 files changed, 57 insertions(+), 8 deletions(-) create mode 100644 changelog/6099.bugfix.rst diff --git a/changelog/6099.bugfix.rst b/changelog/6099.bugfix.rst new file mode 100644 index 000000000..77f33cde1 --- /dev/null +++ b/changelog/6099.bugfix.rst @@ -0,0 +1 @@ +Fix ``--trace`` when used with parametrized functions. diff --git a/src/_pytest/debugging.py b/src/_pytest/debugging.py index 2e3d49c37..3f3c58b16 100644 --- a/src/_pytest/debugging.py +++ b/src/_pytest/debugging.py @@ -1,5 +1,6 @@ """ interactive debugging with PDB, the Python Debugger. """ import argparse +import functools import pdb import sys from doctest import UnexpectedException @@ -274,13 +275,16 @@ class PdbTrace: 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 can't just return `partial(pdb.runcall, testfunction)` because (on + # python < 3.7.4) runcall's first param is `func`, which means we'd get + # an exception if one of the kwargs to testfunction was called `func` + @functools.wraps(testfunction) + def wrapper(*args, **kwargs): + func = functools.partial(testfunction, *args, **kwargs) + _pdb.runcall(func) + + pyfuncitem.obj = wrapper def _enter_pdb(node, excinfo, rep): diff --git a/testing/test_pdb.py b/testing/test_pdb.py index 924c2f4af..d86ba396b 100644 --- a/testing/test_pdb.py +++ b/testing/test_pdb.py @@ -1025,6 +1025,51 @@ class TestTraceOption: 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.""" @@ -1150,7 +1195,6 @@ def test_pdbcls_via_local_module(testdir): def runcall(self, *args, **kwds): print("runcall_called", args, kwds) - assert "func" in kwds """, ) result = testdir.runpytest(