show a short and nice traceback for funcarg lookup errors
--HG-- branch : trunk
This commit is contained in:
parent
7bd60b5abb
commit
f95877a09b
|
@ -1,6 +1,7 @@
|
||||||
Changes between 1.2.1 and 1.2.0
|
Changes between 1.2.1 and 1.2.0
|
||||||
=====================================
|
=====================================
|
||||||
|
|
||||||
|
- display a short and concise traceback if a funcarg lookup fails
|
||||||
- early-load "test*/conftest.py" files, i.e. conftest.py files in
|
- early-load "test*/conftest.py" files, i.e. conftest.py files in
|
||||||
directories starting with 'test'. allows to conveniently keep and access
|
directories starting with 'test'. allows to conveniently keep and access
|
||||||
test-related options without having to put a conftest.py into the package root dir.
|
test-related options without having to put a conftest.py into the package root dir.
|
||||||
|
|
|
@ -64,7 +64,7 @@ class FuncargRequest:
|
||||||
_argprefix = "pytest_funcarg__"
|
_argprefix = "pytest_funcarg__"
|
||||||
_argname = None
|
_argname = None
|
||||||
|
|
||||||
class Error(LookupError):
|
class LookupError(LookupError):
|
||||||
""" error on performing funcarg request. """
|
""" error on performing funcarg request. """
|
||||||
|
|
||||||
def __init__(self, pyfuncitem):
|
def __init__(self, pyfuncitem):
|
||||||
|
@ -170,7 +170,6 @@ class FuncargRequest:
|
||||||
if name not in available:
|
if name not in available:
|
||||||
available.append(name)
|
available.append(name)
|
||||||
fspath, lineno, msg = self._pyfuncitem.reportinfo()
|
fspath, lineno, msg = self._pyfuncitem.reportinfo()
|
||||||
line = "%s:%s" %(fspath, lineno)
|
msg = "LookupError: no factory found for function argument %r" % (argname,)
|
||||||
msg = "funcargument %r not found for: %s" %(argname, line)
|
|
||||||
msg += "\n available funcargs: %s" %(", ".join(available),)
|
msg += "\n available funcargs: %s" %(", ".join(available),)
|
||||||
raise self.Error(msg)
|
raise self.LookupError(msg)
|
||||||
|
|
|
@ -5,6 +5,7 @@ import py
|
||||||
import inspect
|
import inspect
|
||||||
from py._test.collect import configproperty, warnoldcollect
|
from py._test.collect import configproperty, warnoldcollect
|
||||||
from py._test import funcargs
|
from py._test import funcargs
|
||||||
|
from py._code.code import TerminalRepr
|
||||||
|
|
||||||
class PyobjMixin(object):
|
class PyobjMixin(object):
|
||||||
def obj():
|
def obj():
|
||||||
|
@ -252,12 +253,38 @@ class FunctionMixin(PyobjMixin):
|
||||||
traceback = ntraceback.filter()
|
traceback = ntraceback.filter()
|
||||||
return traceback
|
return traceback
|
||||||
|
|
||||||
|
def _repr_failure_py(self, excinfo):
|
||||||
|
if excinfo.errisinstance(funcargs.FuncargRequest.LookupError):
|
||||||
|
fspath, lineno, msg = self.reportinfo()
|
||||||
|
lines, _ = inspect.getsourcelines(self.obj)
|
||||||
|
for i, line in enumerate(lines):
|
||||||
|
if line.strip().startswith('def'):
|
||||||
|
return FuncargLookupErrorRepr(fspath, lineno,
|
||||||
|
lines[:i+1], str(excinfo.value))
|
||||||
|
return super(FunctionMixin, self)._repr_failure_py(excinfo)
|
||||||
|
|
||||||
def repr_failure(self, excinfo, outerr=None):
|
def repr_failure(self, excinfo, outerr=None):
|
||||||
assert outerr is None, "XXX outerr usage is deprecated"
|
assert outerr is None, "XXX outerr usage is deprecated"
|
||||||
return self._repr_failure_py(excinfo)
|
return self._repr_failure_py(excinfo)
|
||||||
|
|
||||||
shortfailurerepr = "F"
|
shortfailurerepr = "F"
|
||||||
|
|
||||||
|
class FuncargLookupErrorRepr(TerminalRepr):
|
||||||
|
def __init__(self, filename, firstlineno, deflines, errorstring):
|
||||||
|
self.deflines = deflines
|
||||||
|
self.errorstring = errorstring
|
||||||
|
self.filename = filename
|
||||||
|
self.firstlineno = firstlineno
|
||||||
|
|
||||||
|
def toterminal(self, tw):
|
||||||
|
tw.line()
|
||||||
|
for line in self.deflines:
|
||||||
|
tw.line(" " + line.strip())
|
||||||
|
for line in self.errorstring.split("\n"):
|
||||||
|
tw.line(" " + line.strip(), red=True)
|
||||||
|
tw.line()
|
||||||
|
tw.line("%s:%d" % (self.filename, self.firstlineno+1))
|
||||||
|
|
||||||
class Generator(FunctionMixin, PyCollectorMixin, py.test.collect.Collector):
|
class Generator(FunctionMixin, PyCollectorMixin, py.test.collect.Collector):
|
||||||
def collect(self):
|
def collect(self):
|
||||||
# test generators are seen as collectors but they also
|
# test generators are seen as collectors but they also
|
||||||
|
|
|
@ -30,7 +30,8 @@ class TestFillFuncArgs:
|
||||||
return 42
|
return 42
|
||||||
""")
|
""")
|
||||||
item = testdir.getitem("def test_func(some): pass")
|
item = testdir.getitem("def test_func(some): pass")
|
||||||
exc = py.test.raises(LookupError, "funcargs.fillfuncargs(item)")
|
exc = py.test.raises(funcargs.FuncargRequest.LookupError,
|
||||||
|
"funcargs.fillfuncargs(item)")
|
||||||
s = str(exc.value)
|
s = str(exc.value)
|
||||||
assert s.find("xyzsomething") != -1
|
assert s.find("xyzsomething") != -1
|
||||||
|
|
||||||
|
@ -560,3 +561,18 @@ def test_funcarg_non_pycollectobj(testdir): # rough jstests usage
|
||||||
funcargs.fillfuncargs(clscol)
|
funcargs.fillfuncargs(clscol)
|
||||||
assert clscol.funcargs['arg1'] == 42
|
assert clscol.funcargs['arg1'] == 42
|
||||||
|
|
||||||
|
|
||||||
|
def test_funcarg_lookup_error(testdir):
|
||||||
|
p = testdir.makepyfile("""
|
||||||
|
def test_lookup_error(unknown):
|
||||||
|
pass
|
||||||
|
""")
|
||||||
|
result = testdir.runpytest()
|
||||||
|
result.stdout.fnmatch_lines([
|
||||||
|
"*ERROR at setup of test_lookup_error*",
|
||||||
|
"*def test_lookup_error(unknown):*",
|
||||||
|
"*LookupError: no factory found*unknown*",
|
||||||
|
"*available funcargs*",
|
||||||
|
"*1 error*",
|
||||||
|
])
|
||||||
|
assert "INTERNAL" not in result.stdout.str()
|
||||||
|
|
Loading…
Reference in New Issue