make the default non-error pass simpler and faster, refine error reporting by presenting "fixture" tracebacks

This commit is contained in:
holger krekel 2012-10-05 14:24:45 +02:00
parent bb07ba7807
commit d1a3f5c3a6
3 changed files with 59 additions and 62 deletions

View File

@ -310,16 +310,7 @@ class Node(object):
def _repr_failure_py(self, excinfo, style=None):
fm = self.session._fixturemanager
if excinfo.errisinstance(fm.FixtureLookupError):
function = excinfo.value.function
factblines = excinfo.value.factblines
if function is not None:
fspath, lineno = getfslineno(function)
lines, _ = inspect.getsourcelines(function)
for i, line in enumerate(lines):
if line.strip().startswith('def'):
return fm.FixtureLookupErrorRepr(fspath,
lineno, lines[:i+1],
str(excinfo.value.msg), factblines)
return excinfo.value.formatrepr()
if self.config.option.fulltrace:
style="long"
else:

View File

@ -993,20 +993,12 @@ class FixtureRequest(FuncargnamesCompatAttr):
def _getfixturedeflist(self, argname):
fixturedeflist = self._arg2fixturedeflist.get(argname, None)
getfixturetb = None
function = None
if fixturedeflist is None:
if self._fixturestack:
function = self._fixturestack[-1].func
getfixturetb = lambda: self._fixturestack[:-1]
else:
function = self.function
fixturedeflist = self._fixturemanager.getfixturedeflist(
argname, self._parentid)
self._arg2fixturedeflist[argname] = fixturedeflist
if not fixturedeflist:
self._fixturemanager._raiselookupfailed(argname, function,
self._parentid, getfixturetb)
raise FixtureLookupError(argname, self)
return fixturedeflist
@property
@ -1085,8 +1077,7 @@ class FixtureRequest(FuncargnamesCompatAttr):
def raiseerror(self, msg):
""" raise a FixtureLookupError with the given message. """
raise self._fixturemanager.FixtureLookupError(self.function, msg)
raise self._fixturemanager.FixtureLookupError(None, self, msg)
def _fillfixtures(self):
item = self._pyfuncitem
@ -1250,32 +1241,58 @@ def slice_kwargs(names, kwargs):
return new_kwargs
class FixtureLookupError(LookupError):
""" could not find a factory. """
def __init__(self, function, msg, factblines=None):
self.function = function
""" could not return a requested Fixture (missing or invalid). """
def __init__(self, argname, request, msg=None):
self.argname = argname
self.request = request
self.fixturestack = list(request._fixturestack)
self.msg = msg
self.factblines = factblines
def formatrepr(self):
tblines = []
addline = tblines.append
stack = [self.request._pyfuncitem.obj]
stack.extend(map(lambda x: x.func, self.fixturestack))
msg = self.msg
if msg is not None:
stack = stack[:-1] # the last fixture raise an error, let's present
# it at the requesting side
for function in stack:
fspath, lineno = getfslineno(function)
lines, _ = inspect.getsourcelines(function)
addline("file %s, line %s" % (fspath, lineno+1))
for i, line in enumerate(lines):
line = line.rstrip()
addline(" " + line)
if line.lstrip().startswith('def'):
break
if msg is None:
fm = self.request._fixturemanager
nodeid = self.request._parentid
available = []
for name, fixturedef in fm.arg2fixturedeflist.items():
faclist = list(fm._matchfactories(fixturedef, self.request._parentid))
if faclist:
available.append(name)
msg = "fixture %r not found" % (self.argname,)
msg += "\n available fixtures: %s" %(", ".join(available),)
msg += "\n use 'py.test --fixtures [testpath]' for help on them."
return FixtureLookupErrorRepr(fspath, lineno, tblines, msg, self.argname)
class FixtureLookupErrorRepr(TerminalRepr):
def __init__(self, filename, firstlineno, deflines, errorstring, factblines):
self.deflines = deflines
def __init__(self, filename, firstlineno, tblines, errorstring, argname):
self.tblines = tblines
self.errorstring = errorstring
self.filename = filename
self.firstlineno = firstlineno
self.factblines = factblines
self.argname = argname
def toterminal(self, tw):
tw.line()
if self.factblines:
tw.line(' dependency of:')
for fixturedef in self.factblines:
tw.line(' %s in %s' % (
fixturedef.argname,
fixturedef.baseid,
))
tw.line()
for line in self.deflines:
tw.line(" " + line.strip())
#tw.line("FixtureLookupError: %s" %(self.argname), red=True)
for tbline in self.tblines:
tw.line(tbline.rstrip())
for line in self.errorstring.split("\n"):
tw.line(" " + line.strip(), red=True)
tw.line()
@ -1436,18 +1453,6 @@ class FixtureManager:
if nodeid.startswith(fixturedef.baseid):
yield fixturedef
def _raiselookupfailed(self, argname, function, nodeid, getfixturetb=None):
available = []
for name, fixturedef in self.arg2fixturedeflist.items():
faclist = list(self._matchfactories(fixturedef, nodeid))
if faclist:
available.append(name)
msg = "LookupError: no factory found for argument %r" % (argname,)
msg += "\n available funcargs: %s" %(", ".join(available),)
msg += "\n use 'py.test --fixtures [testpath]' for help on them."
lines = getfixturetb and getfixturetb() or []
raise FixtureLookupError(function, msg, lines)
def addargfinalizer(self, finalizer, argname):
l = self._arg2finish.setdefault(argname, [])
l.append(finalizer)

View File

@ -559,7 +559,7 @@ class TestFillFixtures:
assert result.ret != 0
result.stdout.fnmatch_lines([
"*def test_func(some)*",
"*LookupError*",
"*fixture*some*not found*",
"*xyzsomething*",
])
@ -1416,8 +1416,8 @@ def test_funcarg_lookup_error(testdir):
result.stdout.fnmatch_lines([
"*ERROR*test_lookup_error*",
"*def test_lookup_error(unknown):*",
"*LookupError: no factory found*unknown*",
"*available funcargs*",
"*fixture*unknown*not found*",
"*available fixtures*",
"*1 error*",
])
assert "INTERNAL" not in result.stdout.str()
@ -1750,12 +1750,13 @@ class TestFixtureFactory:
pass
""")
result = testdir.runpytest()
result.stdout.fnmatch_lines([
"*dependency of:*",
"*call_fail*",
"*def fail(*",
"*LookupError: no factory found for argument 'missing'",
])
result.stdout.fnmatch_lines("""
*pytest.fixture()*
*def call_fail(fail)*
*pytest.fixture()*
*def fail*
*fixture*'missing'*not found*
""")
def test_factory_setup_as_classes(self, testdir):
testdir.makepyfile("""
@ -2585,7 +2586,7 @@ class TestErrors:
assert result.ret != 0
result.stdout.fnmatch_lines([
"*def gen(qwe123):*",
"*no factory*qwe123*",
"*fixture*qwe123*not found*",
"*1 error*",
])
@ -2602,7 +2603,7 @@ class TestErrors:
assert result.ret != 0
result.stdout.fnmatch_lines([
"*def gen(qwe123):*",
"*no factory*qwe123*",
"*fixture*qwe123*not found*",
"*1 error*",
])