improve error representation for missing factory definitions
in recursive funcarg reconstruction
This commit is contained in:
parent
172505f703
commit
dd268c1b2b
|
@ -1,2 +1,2 @@
|
|||
#
|
||||
__version__ = '2.3.0.dev9'
|
||||
__version__ = '2.3.0.dev10'
|
||||
|
|
|
@ -285,6 +285,7 @@ class Node(object):
|
|||
fm = self.session.funcargmanager
|
||||
if excinfo.errisinstance(fm.FuncargLookupError):
|
||||
function = excinfo.value.function
|
||||
factblines = excinfo.value.factblines
|
||||
if function is not None:
|
||||
fspath, lineno = getfslineno(function)
|
||||
lines, _ = inspect.getsourcelines(function)
|
||||
|
@ -292,7 +293,7 @@ class Node(object):
|
|||
if line.strip().startswith('def'):
|
||||
return fm.FuncargLookupErrorRepr(fspath,
|
||||
lineno, lines[:i+1],
|
||||
str(excinfo.value.msg))
|
||||
str(excinfo.value.msg), factblines)
|
||||
if self.config.option.fulltrace:
|
||||
style="long"
|
||||
else:
|
||||
|
|
|
@ -935,17 +935,23 @@ class FuncargRequest:
|
|||
def _getfaclist(self, argname):
|
||||
facdeflist = self._name2factory.get(argname, None)
|
||||
if facdeflist is None:
|
||||
if self._factorystack:
|
||||
function = self._factorystack[-1].func
|
||||
getfactb = lambda: self._factorystack[:-1]
|
||||
else:
|
||||
function = self.function
|
||||
getfactb = None
|
||||
facdeflist = self.funcargmanager.getfactorylist(
|
||||
argname, self.parentid, self.function)
|
||||
argname, self.parentid, function, getfactb)
|
||||
self._name2factory[argname] = facdeflist
|
||||
elif not facdeflist:
|
||||
self.funcargmanager._raiselookupfailed(argname, self.function,
|
||||
self.parentid)
|
||||
return facdeflist
|
||||
|
||||
def raiseerror(self, msg):
|
||||
""" raise a FuncargLookupError with the given message. """
|
||||
raise self.funcargmanager.FuncargLookupError(self.function, msg)
|
||||
#def raiseerror(self, msg):
|
||||
# """ raise a FuncargLookupError with the given message. """
|
||||
# raise self.funcargmanager.FuncargLookupError(self.function, msg)
|
||||
|
||||
@property
|
||||
def function(self):
|
||||
|
@ -1102,14 +1108,7 @@ class FuncargRequest:
|
|||
__tracebackhide__ = True
|
||||
if scopemismatch(self.scope, scope):
|
||||
# try to report something helpful
|
||||
lines = []
|
||||
for factorydef in self._factorystack:
|
||||
factory = factorydef.func
|
||||
fs, lineno = getfslineno(factory)
|
||||
p = self._pyfuncitem.session.fspath.bestrelpath(fs)
|
||||
args = inspect.formatargspec(*inspect.getargspec(factory))
|
||||
lines.append("%s:%d\n def %s%s" %(
|
||||
p, lineno, factory.__name__, args))
|
||||
lines = self._factorytraceback()
|
||||
raise ScopeMismatchError("You tried to access the %r scoped "
|
||||
"funcarg %r with a %r scoped request object, "
|
||||
"involved factories\n%s" %(
|
||||
|
@ -1129,6 +1128,18 @@ class FuncargRequest:
|
|||
self._funcargs[argname] = val
|
||||
return val
|
||||
|
||||
def _factorytraceback(self):
|
||||
lines = []
|
||||
for factorydef in self._factorystack:
|
||||
factory = factorydef.func
|
||||
fs, lineno = getfslineno(factory)
|
||||
p = self._pyfuncitem.session.fspath.bestrelpath(fs)
|
||||
args = inspect.formatargspec(*inspect.getargspec(factory))
|
||||
lines.append("%s:%d: def %s%s" %(
|
||||
p, lineno, factory.__name__, args))
|
||||
return lines
|
||||
|
||||
|
||||
def _getscopeitem(self, scope):
|
||||
if scope == "function":
|
||||
return self._pyfuncitem
|
||||
|
@ -1178,19 +1189,23 @@ def slice_kwargs(names, kwargs):
|
|||
|
||||
class FuncargLookupError(LookupError):
|
||||
""" could not find a factory. """
|
||||
def __init__(self, function, msg):
|
||||
def __init__(self, function, msg, factblines=None):
|
||||
self.function = function
|
||||
self.msg = msg
|
||||
self.factblines = factblines
|
||||
|
||||
class FuncargLookupErrorRepr(TerminalRepr):
|
||||
def __init__(self, filename, firstlineno, deflines, errorstring):
|
||||
def __init__(self, filename, firstlineno, deflines, errorstring, factblines):
|
||||
self.deflines = deflines
|
||||
self.errorstring = errorstring
|
||||
self.filename = filename
|
||||
self.firstlineno = firstlineno
|
||||
self.factblines = factblines
|
||||
|
||||
def toterminal(self, tw):
|
||||
tw.line()
|
||||
for line in self.factblines or []:
|
||||
tw.line(line)
|
||||
for line in self.deflines:
|
||||
tw.line(" " + line.strip())
|
||||
for line in self.errorstring.split("\n"):
|
||||
|
@ -1326,12 +1341,12 @@ class FuncargManager:
|
|||
return l, allargnames
|
||||
|
||||
|
||||
def getfactorylist(self, argname, nodeid, function, raising=True):
|
||||
def getfactorylist(self, argname, nodeid, function, getfactb=None, raising=True):
|
||||
try:
|
||||
factorydeflist = self.arg2facspec[argname]
|
||||
except KeyError:
|
||||
if raising:
|
||||
self._raiselookupfailed(argname, function, nodeid)
|
||||
self._raiselookupfailed(argname, function, nodeid, getfactb)
|
||||
else:
|
||||
return self._matchfactories(factorydeflist, nodeid)
|
||||
|
||||
|
@ -1343,7 +1358,7 @@ class FuncargManager:
|
|||
l.append(factorydef)
|
||||
return l
|
||||
|
||||
def _raiselookupfailed(self, argname, function, nodeid):
|
||||
def _raiselookupfailed(self, argname, function, nodeid, getfactb=None):
|
||||
available = []
|
||||
for name, facdef in self.arg2facspec.items():
|
||||
faclist = self._matchfactories(facdef, nodeid)
|
||||
|
@ -1352,7 +1367,8 @@ class FuncargManager:
|
|||
msg = "LookupError: no factory found for argument %r" % (argname,)
|
||||
msg += "\n available funcargs: %s" %(", ".join(available),)
|
||||
msg += "\n use 'py.test --funcargs [testpath]' for help on them."
|
||||
raise FuncargLookupError(function, msg)
|
||||
lines = getfactb and getfactb() or []
|
||||
raise FuncargLookupError(function, msg, lines)
|
||||
|
||||
def ensure_setupcalls(self, request):
|
||||
setuplist, allnames = self.getsetuplist(request._pyfuncitem.nodeid)
|
||||
|
|
2
setup.py
2
setup.py
|
@ -24,7 +24,7 @@ def main():
|
|||
name='pytest',
|
||||
description='py.test: simple powerful testing with Python',
|
||||
long_description = long_description,
|
||||
version='2.3.0.dev9',
|
||||
version='2.3.0.dev10',
|
||||
url='http://pytest.org',
|
||||
license='MIT license',
|
||||
platforms=['unix', 'linux', 'osx', 'cygwin', 'win32'],
|
||||
|
|
|
@ -2319,7 +2319,7 @@ class TestTestContextScopeAccess:
|
|||
reprec = testdir.inline_run()
|
||||
reprec.assertoutcome(passed=1)
|
||||
|
||||
def test_resource(self, testdir, scope, ok, error):
|
||||
def test_funcarg(self, testdir, scope, ok, error):
|
||||
testdir.makepyfile("""
|
||||
import pytest
|
||||
@pytest.factory(scope=%r)
|
||||
|
@ -2337,6 +2337,23 @@ class TestTestContextScopeAccess:
|
|||
reprec = testdir.inline_run()
|
||||
reprec.assertoutcome(passed=1)
|
||||
|
||||
|
||||
def test_illdefined_factory(testdir):
|
||||
testdir.makepyfile("""
|
||||
import pytest
|
||||
@pytest.factory()
|
||||
def gen(request):
|
||||
return 1
|
||||
def test_something(gen):
|
||||
pass
|
||||
""")
|
||||
result = testdir.runpytest()
|
||||
assert result.ret != 0
|
||||
result.stdout.fnmatch_lines([
|
||||
"*def gen(request):*",
|
||||
"*no factory*request*",
|
||||
])
|
||||
|
||||
class TestTestContextVarious:
|
||||
def test_newstyle_no_request(self, testdir):
|
||||
testdir.makepyfile("""
|
||||
|
|
Loading…
Reference in New Issue