Merge master into features
Conflicts: src/_pytest/debugging.py
This commit is contained in:
commit
4e45472405
|
@ -60,7 +60,7 @@ jobs:
|
||||||
- env: TOXENV=py37-freeze
|
- env: TOXENV=py37-freeze
|
||||||
|
|
||||||
- env: TOXENV=py38-xdist
|
- env: TOXENV=py38-xdist
|
||||||
python: '3.8-dev'
|
python: '3.8'
|
||||||
|
|
||||||
- stage: baseline
|
- stage: baseline
|
||||||
env: TOXENV=py36-xdist
|
env: TOXENV=py36-xdist
|
||||||
|
@ -94,11 +94,6 @@ jobs:
|
||||||
tags: true
|
tags: true
|
||||||
repo: pytest-dev/pytest
|
repo: pytest-dev/pytest
|
||||||
|
|
||||||
matrix:
|
|
||||||
allow_failures:
|
|
||||||
- python: '3.8-dev'
|
|
||||||
env: TOXENV=py38-xdist
|
|
||||||
|
|
||||||
before_script:
|
before_script:
|
||||||
- |
|
- |
|
||||||
# Do not (re-)upload coverage with cron runs.
|
# Do not (re-)upload coverage with cron runs.
|
||||||
|
|
|
@ -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
|
file is managed by towncrier. You *may* edit previous change logs to
|
||||||
fix problems like typo corrections or such.
|
fix problems like typo corrections or such.
|
||||||
To add a new change log entry, please see
|
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
|
we named the news folder changelog
|
||||||
|
|
||||||
.. towncrier release notes start
|
.. towncrier release notes start
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
Fix ``--trace`` when used with parametrized functions.
|
|
@ -137,7 +137,7 @@ class Frame:
|
||||||
def exec_(self, code, **vars):
|
def exec_(self, code, **vars):
|
||||||
""" exec 'code' in the frame
|
""" 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 = self.f_locals.copy()
|
||||||
f_locals.update(vars)
|
f_locals.update(vars)
|
||||||
|
@ -207,7 +207,7 @@ class TracebackEntry:
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def locals(self):
|
def locals(self):
|
||||||
""" locals of underlaying frame """
|
""" locals of underlying frame """
|
||||||
return self.frame.f_locals
|
return self.frame.f_locals
|
||||||
|
|
||||||
def getfirstlinesource(self):
|
def getfirstlinesource(self):
|
||||||
|
@ -274,7 +274,7 @@ class TracebackEntry:
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def name(self):
|
def name(self):
|
||||||
""" co_name of underlaying code """
|
""" co_name of underlying code """
|
||||||
return self.frame.code.raw.co_name
|
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):
|
def cut(self, path=None, lineno=None, firstlineno=None, excludepath=None):
|
||||||
""" return a Traceback instance wrapping part of this Traceback
|
""" 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
|
first frame to start the to-be-returned traceback is determined
|
||||||
|
|
||||||
this allows cutting the first part of a Traceback instance e.g.
|
this allows cutting the first part of a Traceback instance e.g.
|
||||||
|
@ -1008,7 +1008,7 @@ class ReprFileLocation(TerminalRepr):
|
||||||
|
|
||||||
def toterminal(self, tw) -> None:
|
def toterminal(self, tw) -> None:
|
||||||
# filename and lineno output for each entry,
|
# 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
|
msg = self.message
|
||||||
i = msg.find("\n")
|
i = msg.find("\n")
|
||||||
if i != -1:
|
if i != -1:
|
||||||
|
|
|
@ -38,7 +38,6 @@ def format_explanation(explanation: str) -> str:
|
||||||
for when one explanation needs to span multiple lines, e.g. when
|
for when one explanation needs to span multiple lines, e.g. when
|
||||||
displaying diffs.
|
displaying diffs.
|
||||||
"""
|
"""
|
||||||
explanation = explanation
|
|
||||||
lines = _split_explanation(explanation)
|
lines = _split_explanation(explanation)
|
||||||
result = _format_lines(lines)
|
result = _format_lines(lines)
|
||||||
return "\n".join(result)
|
return "\n".join(result)
|
||||||
|
|
|
@ -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 sys
|
import sys
|
||||||
|
|
||||||
from _pytest import outcomes
|
from _pytest import outcomes
|
||||||
|
@ -278,13 +279,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):
|
||||||
|
|
|
@ -513,7 +513,7 @@ class LogXML:
|
||||||
key = nodeid, slavenode
|
key = nodeid, slavenode
|
||||||
|
|
||||||
if key in self.node_reporters:
|
if key in self.node_reporters:
|
||||||
# TODO: breasks for --dist=each
|
# TODO: breaks for --dist=each
|
||||||
return self.node_reporters[key]
|
return self.node_reporters[key]
|
||||||
|
|
||||||
reporter = _NodeReporter(nodeid, self)
|
reporter = _NodeReporter(nodeid, self)
|
||||||
|
|
|
@ -437,7 +437,7 @@ class Session(nodes.FSCollector):
|
||||||
# one or more conftests are not in use at this fspath
|
# one or more conftests are not in use at this fspath
|
||||||
proxy = FSHookProxy(fspath, pm, remove_mods)
|
proxy = FSHookProxy(fspath, pm, remove_mods)
|
||||||
else:
|
else:
|
||||||
# all plugis are active for this fspath
|
# all plugins are active for this fspath
|
||||||
proxy = self.config.hook
|
proxy = self.config.hook
|
||||||
return proxy
|
return proxy
|
||||||
|
|
||||||
|
|
|
@ -28,7 +28,7 @@ class MarkEvaluator:
|
||||||
self._mark_name = name
|
self._mark_name = name
|
||||||
|
|
||||||
def __bool__(self):
|
def __bool__(self):
|
||||||
# dont cache here to prevent staleness
|
# don't cache here to prevent staleness
|
||||||
return bool(self._get_marks())
|
return bool(self._get_marks())
|
||||||
|
|
||||||
__nonzero__ = __bool__
|
__nonzero__ = __bool__
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
"""
|
"""
|
||||||
this is a place where we put datastructures used by legacy apis
|
this is a place where we put datastructures used by legacy apis
|
||||||
we hope ot remove
|
we hope to remove
|
||||||
"""
|
"""
|
||||||
import keyword
|
import keyword
|
||||||
|
|
||||||
|
|
|
@ -211,8 +211,8 @@ def pytest_pycollect_makeitem(collector, name, obj):
|
||||||
# mock seems to store unbound methods (issue473), normalize it
|
# mock seems to store unbound methods (issue473), normalize it
|
||||||
obj = getattr(obj, "__func__", obj)
|
obj = getattr(obj, "__func__", obj)
|
||||||
# We need to try and unwrap the function if it's a functools.partial
|
# We need to try and unwrap the function if it's a functools.partial
|
||||||
# or a funtools.wrapped.
|
# or a functools.wrapped.
|
||||||
# We musn't if it's been wrapped with mock.patch (python 2 only)
|
# 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))):
|
if not (inspect.isfunction(obj) or inspect.isfunction(get_real_func(obj))):
|
||||||
filename, lineno = getfslineno(obj)
|
filename, lineno = getfslineno(obj)
|
||||||
warnings.warn_explicit(
|
warnings.warn_explicit(
|
||||||
|
@ -596,7 +596,7 @@ class Package(Module):
|
||||||
# one or more conftests are not in use at this fspath
|
# one or more conftests are not in use at this fspath
|
||||||
proxy = FSHookProxy(fspath, pm, remove_mods)
|
proxy = FSHookProxy(fspath, pm, remove_mods)
|
||||||
else:
|
else:
|
||||||
# all plugis are active for this fspath
|
# all plugins are active for this fspath
|
||||||
proxy = self.config.hook
|
proxy = self.config.hook
|
||||||
return proxy
|
return proxy
|
||||||
|
|
||||||
|
|
|
@ -122,7 +122,7 @@ def pytest_runtest_makereport(item, call):
|
||||||
outcome = yield
|
outcome = yield
|
||||||
rep = outcome.get_result()
|
rep = outcome.get_result()
|
||||||
evalxfail = getattr(item, "_evalxfail", None)
|
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 hasattr(item, "_unexpectedsuccess") and rep.when == "call":
|
||||||
|
|
||||||
if item._unexpectedsuccess:
|
if item._unexpectedsuccess:
|
||||||
|
@ -132,7 +132,7 @@ def pytest_runtest_makereport(item, call):
|
||||||
rep.outcome = "failed"
|
rep.outcome = "failed"
|
||||||
|
|
||||||
elif item.config.option.runxfail:
|
elif item.config.option.runxfail:
|
||||||
pass # don't interefere
|
pass # don't interfere
|
||||||
elif call.excinfo and call.excinfo.errisinstance(xfail.Exception):
|
elif call.excinfo and call.excinfo.errisinstance(xfail.Exception):
|
||||||
rep.wasxfail = "reason: " + call.excinfo.value.msg
|
rep.wasxfail = "reason: " + call.excinfo.value.msg
|
||||||
rep.outcome = "skipped"
|
rep.outcome = "skipped"
|
||||||
|
|
|
@ -15,7 +15,7 @@ class TestMetafunc:
|
||||||
def Metafunc(self, func, config=None):
|
def Metafunc(self, func, config=None):
|
||||||
# the unit tests of this class check if things work correctly
|
# the unit tests of this class check if things work correctly
|
||||||
# on the funcarg level, so we don't need a full blown
|
# on the funcarg level, so we don't need a full blown
|
||||||
# initiliazation
|
# initialization
|
||||||
class FixtureInfo:
|
class FixtureInfo:
|
||||||
name2fixturedefs = None
|
name2fixturedefs = None
|
||||||
|
|
||||||
|
|
|
@ -482,7 +482,7 @@ class TestFunctional:
|
||||||
items, rec = testdir.inline_genitems(p)
|
items, rec = testdir.inline_genitems(p)
|
||||||
base_item, sub_item, sub_item_other = items
|
base_item, sub_item, sub_item_other = items
|
||||||
print(items, [x.nodeid for x in 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(base_item.iter_markers(name="b"))
|
||||||
assert not list(sub_item_other.iter_markers(name="b"))
|
assert not list(sub_item_other.iter_markers(name="b"))
|
||||||
assert list(sub_item.iter_markers(name="b"))
|
assert list(sub_item.iter_markers(name="b"))
|
||||||
|
|
|
@ -229,7 +229,7 @@ def test_nose_setup_ordering(testdir):
|
||||||
|
|
||||||
def test_apiwrapper_problem_issue260(testdir):
|
def test_apiwrapper_problem_issue260(testdir):
|
||||||
# this would end up trying a call an optional teardown on the class
|
# 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(
|
testdir.makepyfile(
|
||||||
"""
|
"""
|
||||||
import unittest
|
import unittest
|
||||||
|
|
|
@ -304,7 +304,7 @@ def test_argcomplete(testdir, monkeypatch):
|
||||||
shlex.quote(sys.executable)
|
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
|
# to handle a keyword argument env that replaces os.environ in popen or
|
||||||
# extends the copy, advantage: could not forget to restore
|
# extends the copy, advantage: could not forget to restore
|
||||||
monkeypatch.setenv("_ARGCOMPLETE", "1")
|
monkeypatch.setenv("_ARGCOMPLETE", "1")
|
||||||
|
|
|
@ -603,7 +603,7 @@ class TestPDB:
|
||||||
# No extra newline.
|
# No extra newline.
|
||||||
assert child.before.endswith(b"c\r\nprint_from_foo\r\n")
|
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("debug 42")
|
||||||
child.sendline("q")
|
child.sendline("q")
|
||||||
child.expect("LEAVING RECURSIVE DEBUGGER")
|
child.expect("LEAVING RECURSIVE DEBUGGER")
|
||||||
|
@ -1047,6 +1047,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."""
|
||||||
|
@ -1172,7 +1217,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(
|
||||||
|
|
|
@ -4,7 +4,7 @@ import pytest
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
def stepwise_testdir(testdir):
|
def stepwise_testdir(testdir):
|
||||||
# Rather than having to modify our testfile between tests, we introduce
|
# 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(
|
testdir.makeconftest(
|
||||||
"""
|
"""
|
||||||
def pytest_addoption(parser):
|
def pytest_addoption(parser):
|
||||||
|
|
Loading…
Reference in New Issue