* improve and test --tb=short reporting

* show --tb=short tracebacks for importing test modules

--HG--
branch : trunk
This commit is contained in:
holger krekel 2010-05-22 16:18:24 +02:00
parent 94ce5b0a5a
commit 29a5b7452e
8 changed files with 113 additions and 38 deletions

View File

@ -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

View File

@ -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":
if self.style in ("short", "long"):
short = self.style == "short"
reprargs = None
if not short:
reprargs = self.repr_args(entry)
lines.extend(self.get_source(source, line_index, excinfo))
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 = None
if not short:
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)
return ReprEntry(lines, reprargs, localsrepr, filelocrepr, short)
if excinfo:
lines.extend(self.get_exconly(excinfo, indent=4))
return ReprEntry(lines, None, None, None)
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:

View File

@ -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)

View File

@ -172,10 +172,11 @@ 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 style is None:
if self.config.option.tbstyle == "short":
style = "short"
else:

View File

@ -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"

View File

@ -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("""

View File

@ -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([

View File

@ -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*",
])