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:
		
							parent
							
								
									cefe6bfec3
								
							
						
					
					
						commit
						285524c6cd
					
				| 
						 | 
					@ -0,0 +1 @@
 | 
				
			||||||
 | 
					Fix ``--trace`` when used with parametrized functions.
 | 
				
			||||||
| 
						 | 
					@ -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):
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -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(
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in New Issue