* improve and test --tb=short reporting
* show --tb=short tracebacks for importing test modules --HG-- branch : trunk
This commit is contained in:
parent
94ce5b0a5a
commit
29a5b7452e
|
@ -37,7 +37,9 @@ New features
|
|||
Fixes / Maintenance
|
||||
++++++++++++++++++++++
|
||||
|
||||
- improve tracebacks presentation:
|
||||
- improved traceback presentation:
|
||||
- improved and unified reporting for "--tb=short" option
|
||||
- Errors during test module imports are much shorter, (using --tb=short style)
|
||||
- raises shows shorter more relevant tracebacks
|
||||
|
||||
- improve support for raises and other dynamically compiled code by
|
||||
|
|
|
@ -416,7 +416,7 @@ class FormattedExcinfo(object):
|
|||
args.append((argname, self._saferepr(argvalue)))
|
||||
return ReprFuncArgs(args)
|
||||
|
||||
def get_source(self, source, line_index=-1, excinfo=None):
|
||||
def get_source(self, source, line_index=-1, excinfo=None, short=False):
|
||||
""" return formatted and marked up source lines. """
|
||||
lines = []
|
||||
if source is None:
|
||||
|
@ -428,6 +428,8 @@ class FormattedExcinfo(object):
|
|||
if i == line_index:
|
||||
prefix = self.flow_marker + " "
|
||||
else:
|
||||
if short:
|
||||
continue
|
||||
prefix = " "
|
||||
line = prefix + source[i]
|
||||
lines.append(line)
|
||||
|
@ -482,24 +484,26 @@ class FormattedExcinfo(object):
|
|||
line_index = entry.lineno - max(entry.getfirstlinesource(), 0)
|
||||
|
||||
lines = []
|
||||
if self.style == "long":
|
||||
reprargs = self.repr_args(entry)
|
||||
lines.extend(self.get_source(source, line_index, excinfo))
|
||||
message = excinfo and excinfo.typename or ""
|
||||
if self.style in ("short", "long"):
|
||||
short = self.style == "short"
|
||||
reprargs = None
|
||||
if not short:
|
||||
reprargs = self.repr_args(entry)
|
||||
s = self.get_source(source, line_index, excinfo, short=short)
|
||||
lines.extend(s)
|
||||
if short:
|
||||
message = "in %s" %(entry.name)
|
||||
else:
|
||||
message = excinfo and excinfo.typename or ""
|
||||
path = self._makepath(entry.path)
|
||||
filelocrepr = ReprFileLocation(path, entry.lineno+1, message)
|
||||
localsrepr = self.repr_locals(entry.locals)
|
||||
return ReprEntry(lines, reprargs, localsrepr, filelocrepr)
|
||||
else:
|
||||
if self.style == "short":
|
||||
line = source[line_index].lstrip()
|
||||
basename = os.path.basename(entry.frame.code.filename)
|
||||
lines.append(' File "%s", line %d, in %s' % (
|
||||
basename, entry.lineno+1, entry.name))
|
||||
lines.append(" " + line)
|
||||
if excinfo:
|
||||
lines.extend(self.get_exconly(excinfo, indent=4))
|
||||
return ReprEntry(lines, None, None, None)
|
||||
localsrepr = None
|
||||
if not short:
|
||||
localsrepr = self.repr_locals(entry.locals)
|
||||
return ReprEntry(lines, reprargs, localsrepr, filelocrepr, short)
|
||||
if excinfo:
|
||||
lines.extend(self.get_exconly(excinfo, indent=4))
|
||||
return ReprEntry(lines, None, None, None, False)
|
||||
|
||||
def _makepath(self, path):
|
||||
if not self.abspath:
|
||||
|
@ -595,13 +599,21 @@ class ReprTraceback(TerminalRepr):
|
|||
class ReprEntry(TerminalRepr):
|
||||
localssep = "_ "
|
||||
|
||||
def __init__(self, lines, reprfuncargs, reprlocals, filelocrepr):
|
||||
def __init__(self, lines, reprfuncargs, reprlocals, filelocrepr, short):
|
||||
self.lines = lines
|
||||
self.reprfuncargs = reprfuncargs
|
||||
self.reprlocals = reprlocals
|
||||
self.reprfileloc = filelocrepr
|
||||
self.short = short
|
||||
|
||||
def toterminal(self, tw):
|
||||
if self.short:
|
||||
self.reprfileloc.toterminal(tw)
|
||||
for line in self.lines:
|
||||
red = line.startswith("E ")
|
||||
tw.line(line, bold=True, red=red)
|
||||
#tw.line("")
|
||||
return
|
||||
if self.reprfuncargs:
|
||||
self.reprfuncargs.toterminal(tw)
|
||||
for line in self.lines:
|
||||
|
|
|
@ -188,7 +188,11 @@ class CollectReport(BaseReport):
|
|||
self.passed = True
|
||||
self.result = result
|
||||
else:
|
||||
self.longrepr = self.collector._repr_failure_py(excinfo)
|
||||
style = "short"
|
||||
if collector.config.getvalue("fulltrace"):
|
||||
style = "long"
|
||||
self.longrepr = self.collector._repr_failure_py(excinfo,
|
||||
style=style)
|
||||
if excinfo.errisinstance(py.test.skip.Exception):
|
||||
self.skipped = True
|
||||
self.reason = str(excinfo.value)
|
||||
|
|
|
@ -172,14 +172,15 @@ class Node(object):
|
|||
def _prunetraceback(self, traceback):
|
||||
return traceback
|
||||
|
||||
def _repr_failure_py(self, excinfo):
|
||||
def _repr_failure_py(self, excinfo, style=None):
|
||||
excinfo.traceback = self._prunetraceback(excinfo.traceback)
|
||||
# XXX should excinfo.getrepr record all data and toterminal()
|
||||
# process it?
|
||||
if self.config.option.tbstyle == "short":
|
||||
style = "short"
|
||||
else:
|
||||
style = "long"
|
||||
if style is None:
|
||||
if self.config.option.tbstyle == "short":
|
||||
style = "short"
|
||||
else:
|
||||
style = "long"
|
||||
return excinfo.getrepr(funcargs=True,
|
||||
showlocals=self.config.option.showlocals,
|
||||
style=style)
|
||||
|
|
|
@ -253,7 +253,7 @@ class FunctionMixin(PyobjMixin):
|
|||
traceback = ntraceback.filter()
|
||||
return traceback
|
||||
|
||||
def _repr_failure_py(self, excinfo):
|
||||
def _repr_failure_py(self, excinfo, style="long"):
|
||||
if excinfo.errisinstance(funcargs.FuncargRequest.LookupError):
|
||||
fspath, lineno, msg = self.reportinfo()
|
||||
lines, _ = inspect.getsourcelines(self.obj)
|
||||
|
@ -261,11 +261,13 @@ class FunctionMixin(PyobjMixin):
|
|||
if line.strip().startswith('def'):
|
||||
return FuncargLookupErrorRepr(fspath, lineno,
|
||||
lines[:i+1], str(excinfo.value))
|
||||
return super(FunctionMixin, self)._repr_failure_py(excinfo)
|
||||
return super(FunctionMixin, self)._repr_failure_py(excinfo,
|
||||
style=style)
|
||||
|
||||
def repr_failure(self, excinfo, outerr=None):
|
||||
assert outerr is None, "XXX outerr usage is deprecated"
|
||||
return self._repr_failure_py(excinfo)
|
||||
return self._repr_failure_py(excinfo,
|
||||
style=self.config.getvalue("tbstyle"))
|
||||
|
||||
shortfailurerepr = "F"
|
||||
|
||||
|
|
|
@ -463,16 +463,18 @@ raise ValueError()
|
|||
reprtb = p.repr_traceback_entry(excinfo.traceback[-2])
|
||||
lines = reprtb.lines
|
||||
basename = py.path.local(mod.__file__).basename
|
||||
assert lines[0] == ' File "%s", line 5, in entry' % basename
|
||||
assert lines[1] == ' func1()'
|
||||
assert lines[0] == '> func1()'
|
||||
assert basename in str(reprtb.reprfileloc.path)
|
||||
assert reprtb.reprfileloc.lineno == 5
|
||||
|
||||
# test last entry
|
||||
p = FormattedExcinfo(style="short")
|
||||
reprtb = p.repr_traceback_entry(excinfo.traceback[-1], excinfo)
|
||||
lines = reprtb.lines
|
||||
assert lines[0] == ' File "%s", line 3, in func1' % basename
|
||||
assert lines[1] == ' raise ValueError("hello")'
|
||||
assert lines[2] == 'E ValueError: hello'
|
||||
assert lines[0] == '> raise ValueError("hello")'
|
||||
assert lines[1] == 'E ValueError: hello'
|
||||
assert basename in str(reprtb.reprfileloc.path)
|
||||
assert reprtb.reprfileloc.lineno == 3
|
||||
|
||||
def test_repr_tracebackentry_no(self, importasmod):
|
||||
mod = importasmod("""
|
||||
|
@ -525,12 +527,10 @@ raise ValueError()
|
|||
last_lines = last_reprtb.lines
|
||||
monkeypatch.undo()
|
||||
basename = py.path.local(mod.__file__).basename
|
||||
assert lines[0] == ' File "%s", line 5, in entry' % basename
|
||||
assert lines[1] == ' func1()'
|
||||
assert lines[0] == '> func1()'
|
||||
|
||||
assert last_lines[0] == ' File "%s", line 3, in func1' % basename
|
||||
assert last_lines[1] == ' raise ValueError("hello")'
|
||||
assert last_lines[2] == 'E ValueError: hello'
|
||||
assert last_lines[0] == '> raise ValueError("hello")'
|
||||
assert last_lines[1] == 'E ValueError: hello'
|
||||
|
||||
def test_repr_traceback_and_excinfo(self, importasmod):
|
||||
mod = importasmod("""
|
||||
|
|
|
@ -626,6 +626,35 @@ def test_terminalreporter_reportopt_conftestsetting(testdir):
|
|||
"*1 passed*"
|
||||
])
|
||||
|
||||
def test_tbstyle_short(testdir):
|
||||
p = testdir.makepyfile("""
|
||||
def pytest_funcarg__arg(request):
|
||||
return 42
|
||||
def test_opt(arg):
|
||||
x = 0
|
||||
assert x
|
||||
""")
|
||||
result = testdir.runpytest("--tb=short")
|
||||
s = result.stdout.str()
|
||||
assert 'arg = 42' not in s
|
||||
assert 'x = 0' not in s
|
||||
result.stdout.fnmatch_lines([
|
||||
"*%s:5*" % p.basename,
|
||||
">*assert x",
|
||||
"E*assert*",
|
||||
])
|
||||
result = testdir.runpytest()
|
||||
s = result.stdout.str()
|
||||
assert 'x = 0' in s
|
||||
assert 'assert x' in s
|
||||
|
||||
def test_trace_reporting(testdir):
|
||||
result = testdir.runpytest("--traceconfig")
|
||||
result.stdout.fnmatch_lines([
|
||||
"*active plugins*"
|
||||
])
|
||||
assert result.ret == 0
|
||||
|
||||
def test_trace_reporting(testdir):
|
||||
result = testdir.runpytest("--traceconfig")
|
||||
result.stdout.fnmatch_lines([
|
||||
|
|
|
@ -484,3 +484,28 @@ class TestTracebackCutting:
|
|||
assert out.find("conftest.py:2: ValueError") != -1
|
||||
numentries = out.count("_ _ _ _") # separator for traceback entries
|
||||
assert numentries >3
|
||||
|
||||
def test_traceback_error_during_import(self, testdir):
|
||||
testdir.makepyfile("""
|
||||
x = 1
|
||||
x = 2
|
||||
x = 17
|
||||
asd
|
||||
""")
|
||||
result = testdir.runpytest()
|
||||
assert result.ret != 0
|
||||
out = result.stdout.str()
|
||||
assert "x = 1" not in out
|
||||
assert "x = 2" not in out
|
||||
result.stdout.fnmatch_lines([
|
||||
">*asd*",
|
||||
"E*NameError*",
|
||||
])
|
||||
result = testdir.runpytest("--fulltrace")
|
||||
out = result.stdout.str()
|
||||
assert "x = 1" in out
|
||||
assert "x = 2" in out
|
||||
result.stdout.fnmatch_lines([
|
||||
">*asd*",
|
||||
"E*NameError*",
|
||||
])
|
||||
|
|
Loading…
Reference in New Issue