diff --git a/.travis.yml b/.travis.yml index 32479b7a9..310d7093b 100644 --- a/.travis.yml +++ b/.travis.yml @@ -60,7 +60,7 @@ jobs: - env: TOXENV=py37-freeze - env: TOXENV=py38-xdist - python: '3.8-dev' + python: '3.8' - stage: baseline env: TOXENV=py36-xdist @@ -94,11 +94,6 @@ jobs: tags: true repo: pytest-dev/pytest -matrix: - allow_failures: - - python: '3.8-dev' - env: TOXENV=py38-xdist - before_script: - | # Do not (re-)upload coverage with cron runs. diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 1cc1a26fc..375b5dabf 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -13,7 +13,7 @@ with advance notice in the **Deprecations** section of releases. file is managed by towncrier. You *may* edit previous change logs to fix problems like typo corrections or such. To add a new change log entry, please see - https://pip.pypa.io/en/latest/development/#adding-a-news-entry + https://pip.pypa.io/en/latest/development/contributing/#news-entries we named the news folder changelog .. towncrier release notes start 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/_code/code.py b/src/_pytest/_code/code.py index 1a35521ad..3c2acfe7f 100644 --- a/src/_pytest/_code/code.py +++ b/src/_pytest/_code/code.py @@ -137,7 +137,7 @@ class Frame: def exec_(self, code, **vars): """ exec 'code' in the frame - 'vars' are optiona; additional local variables + 'vars' are optional; additional local variables """ f_locals = self.f_locals.copy() f_locals.update(vars) @@ -207,7 +207,7 @@ class TracebackEntry: @property def locals(self): - """ locals of underlaying frame """ + """ locals of underlying frame """ return self.frame.f_locals def getfirstlinesource(self): @@ -274,7 +274,7 @@ class TracebackEntry: @property def name(self): - """ co_name of underlaying code """ + """ co_name of underlying code """ return self.frame.code.raw.co_name @@ -302,7 +302,7 @@ class Traceback(list): def cut(self, path=None, lineno=None, firstlineno=None, excludepath=None): """ return a Traceback instance wrapping part of this Traceback - by provding any combination of path, lineno and firstlineno, the + by providing any combination of path, lineno and firstlineno, the first frame to start the to-be-returned traceback is determined this allows cutting the first part of a Traceback instance e.g. @@ -1008,7 +1008,7 @@ class ReprFileLocation(TerminalRepr): def toterminal(self, tw) -> None: # filename and lineno output for each entry, - # using an output format that most editors unterstand + # using an output format that most editors understand msg = self.message i = msg.find("\n") if i != -1: diff --git a/src/_pytest/assertion/util.py b/src/_pytest/assertion/util.py index dff275902..7521c08e4 100644 --- a/src/_pytest/assertion/util.py +++ b/src/_pytest/assertion/util.py @@ -38,7 +38,6 @@ def format_explanation(explanation: str) -> str: for when one explanation needs to span multiple lines, e.g. when displaying diffs. """ - explanation = explanation lines = _split_explanation(explanation) result = _format_lines(lines) return "\n".join(result) diff --git a/src/_pytest/debugging.py b/src/_pytest/debugging.py index a56ad4b83..9155d7e98 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 sys from _pytest import outcomes @@ -278,13 +279,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/src/_pytest/junitxml.py b/src/_pytest/junitxml.py index ba0f48ed3..fb951106f 100644 --- a/src/_pytest/junitxml.py +++ b/src/_pytest/junitxml.py @@ -513,7 +513,7 @@ class LogXML: key = nodeid, slavenode if key in self.node_reporters: - # TODO: breasks for --dist=each + # TODO: breaks for --dist=each return self.node_reporters[key] reporter = _NodeReporter(nodeid, self) diff --git a/src/_pytest/main.py b/src/_pytest/main.py index d10d2d871..9594345ca 100644 --- a/src/_pytest/main.py +++ b/src/_pytest/main.py @@ -437,7 +437,7 @@ class Session(nodes.FSCollector): # one or more conftests are not in use at this fspath proxy = FSHookProxy(fspath, pm, remove_mods) else: - # all plugis are active for this fspath + # all plugins are active for this fspath proxy = self.config.hook return proxy diff --git a/src/_pytest/mark/evaluate.py b/src/_pytest/mark/evaluate.py index b9f2d61f8..53822e98f 100644 --- a/src/_pytest/mark/evaluate.py +++ b/src/_pytest/mark/evaluate.py @@ -28,7 +28,7 @@ class MarkEvaluator: self._mark_name = name def __bool__(self): - # dont cache here to prevent staleness + # don't cache here to prevent staleness return bool(self._get_marks()) __nonzero__ = __bool__ diff --git a/src/_pytest/mark/legacy.py b/src/_pytest/mark/legacy.py index d14ea3a82..3721e3b02 100644 --- a/src/_pytest/mark/legacy.py +++ b/src/_pytest/mark/legacy.py @@ -1,6 +1,6 @@ """ this is a place where we put datastructures used by legacy apis -we hope ot remove +we hope to remove """ import keyword diff --git a/src/_pytest/python.py b/src/_pytest/python.py index 61cbfec8a..d7fb3d78e 100644 --- a/src/_pytest/python.py +++ b/src/_pytest/python.py @@ -211,8 +211,8 @@ def pytest_pycollect_makeitem(collector, name, obj): # mock seems to store unbound methods (issue473), normalize it obj = getattr(obj, "__func__", obj) # We need to try and unwrap the function if it's a functools.partial - # or a funtools.wrapped. - # We musn't if it's been wrapped with mock.patch (python 2 only) + # or a functools.wrapped. + # We mustn't if it's been wrapped with mock.patch (python 2 only) if not (inspect.isfunction(obj) or inspect.isfunction(get_real_func(obj))): filename, lineno = getfslineno(obj) warnings.warn_explicit( @@ -596,7 +596,7 @@ class Package(Module): # one or more conftests are not in use at this fspath proxy = FSHookProxy(fspath, pm, remove_mods) else: - # all plugis are active for this fspath + # all plugins are active for this fspath proxy = self.config.hook return proxy diff --git a/src/_pytest/skipping.py b/src/_pytest/skipping.py index 9eaa77d17..f70ef7f59 100644 --- a/src/_pytest/skipping.py +++ b/src/_pytest/skipping.py @@ -122,7 +122,7 @@ def pytest_runtest_makereport(item, call): outcome = yield rep = outcome.get_result() evalxfail = getattr(item, "_evalxfail", None) - # unitttest special case, see setting of _unexpectedsuccess + # unittest special case, see setting of _unexpectedsuccess if hasattr(item, "_unexpectedsuccess") and rep.when == "call": if item._unexpectedsuccess: @@ -132,7 +132,7 @@ def pytest_runtest_makereport(item, call): rep.outcome = "failed" elif item.config.option.runxfail: - pass # don't interefere + pass # don't interfere elif call.excinfo and call.excinfo.errisinstance(xfail.Exception): rep.wasxfail = "reason: " + call.excinfo.value.msg rep.outcome = "skipped" diff --git a/testing/python/metafunc.py b/testing/python/metafunc.py index 4273ab796..5becb0f8c 100644 --- a/testing/python/metafunc.py +++ b/testing/python/metafunc.py @@ -15,7 +15,7 @@ class TestMetafunc: def Metafunc(self, func, config=None): # the unit tests of this class check if things work correctly # on the funcarg level, so we don't need a full blown - # initiliazation + # initialization class FixtureInfo: name2fixturedefs = None diff --git a/testing/test_mark.py b/testing/test_mark.py index 93bc77a16..2c12c0451 100644 --- a/testing/test_mark.py +++ b/testing/test_mark.py @@ -482,7 +482,7 @@ class TestFunctional: items, rec = testdir.inline_genitems(p) base_item, sub_item, sub_item_other = items print(items, [x.nodeid for x in items]) - # new api seregates + # new api segregates assert not list(base_item.iter_markers(name="b")) assert not list(sub_item_other.iter_markers(name="b")) assert list(sub_item.iter_markers(name="b")) diff --git a/testing/test_nose.py b/testing/test_nose.py index 16d8d1fc0..8a30755c9 100644 --- a/testing/test_nose.py +++ b/testing/test_nose.py @@ -229,7 +229,7 @@ def test_nose_setup_ordering(testdir): def test_apiwrapper_problem_issue260(testdir): # this would end up trying a call an optional teardown on the class - # for plain unittests we dont want nose behaviour + # for plain unittests we don't want nose behaviour testdir.makepyfile( """ import unittest diff --git a/testing/test_parseopt.py b/testing/test_parseopt.py index d39065b82..915747378 100644 --- a/testing/test_parseopt.py +++ b/testing/test_parseopt.py @@ -304,7 +304,7 @@ def test_argcomplete(testdir, monkeypatch): shlex.quote(sys.executable) ) ) - # alternative would be exteneded Testdir.{run(),_run(),popen()} to be able + # alternative would be extended Testdir.{run(),_run(),popen()} to be able # to handle a keyword argument env that replaces os.environ in popen or # extends the copy, advantage: could not forget to restore monkeypatch.setenv("_ARGCOMPLETE", "1") diff --git a/testing/test_pdb.py b/testing/test_pdb.py index fbf344807..25d2292e9 100644 --- a/testing/test_pdb.py +++ b/testing/test_pdb.py @@ -603,7 +603,7 @@ class TestPDB: # No extra newline. assert child.before.endswith(b"c\r\nprint_from_foo\r\n") - # set_debug should not raise outcomes.Exit, if used recrursively. + # set_debug should not raise outcomes. Exit, if used recursively. child.sendline("debug 42") child.sendline("q") child.expect("LEAVING RECURSIVE DEBUGGER") @@ -1047,6 +1047,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 "= \x1b[32m\x1b[1m6 passed\x1b[0m\x1b[32m 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.""" @@ -1172,7 +1217,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( diff --git a/testing/test_stepwise.py b/testing/test_stepwise.py index 3e4f86f21..8d1b82f70 100644 --- a/testing/test_stepwise.py +++ b/testing/test_stepwise.py @@ -4,7 +4,7 @@ import pytest @pytest.fixture def stepwise_testdir(testdir): # Rather than having to modify our testfile between tests, we introduce - # a flag for wether or not the second test should fail. + # a flag for whether or not the second test should fail. testdir.makeconftest( """ def pytest_addoption(parser):