* 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 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 - raises shows shorter more relevant tracebacks
- improve support for raises and other dynamically compiled code by - 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))) args.append((argname, self._saferepr(argvalue)))
return ReprFuncArgs(args) 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. """ """ return formatted and marked up source lines. """
lines = [] lines = []
if source is None: if source is None:
@ -428,6 +428,8 @@ class FormattedExcinfo(object):
if i == line_index: if i == line_index:
prefix = self.flow_marker + " " prefix = self.flow_marker + " "
else: else:
if short:
continue
prefix = " " prefix = " "
line = prefix + source[i] line = prefix + source[i]
lines.append(line) lines.append(line)
@ -482,24 +484,26 @@ class FormattedExcinfo(object):
line_index = entry.lineno - max(entry.getfirstlinesource(), 0) line_index = entry.lineno - max(entry.getfirstlinesource(), 0)
lines = [] lines = []
if self.style == "long": if self.style in ("short", "long"):
reprargs = self.repr_args(entry) short = self.style == "short"
lines.extend(self.get_source(source, line_index, excinfo)) reprargs = None
message = excinfo and excinfo.typename or "" 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) path = self._makepath(entry.path)
filelocrepr = ReprFileLocation(path, entry.lineno+1, message) filelocrepr = ReprFileLocation(path, entry.lineno+1, message)
localsrepr = self.repr_locals(entry.locals) localsrepr = None
return ReprEntry(lines, reprargs, localsrepr, filelocrepr) if not short:
else: localsrepr = self.repr_locals(entry.locals)
if self.style == "short": return ReprEntry(lines, reprargs, localsrepr, filelocrepr, short)
line = source[line_index].lstrip() if excinfo:
basename = os.path.basename(entry.frame.code.filename) lines.extend(self.get_exconly(excinfo, indent=4))
lines.append(' File "%s", line %d, in %s' % ( return ReprEntry(lines, None, None, None, False)
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)
def _makepath(self, path): def _makepath(self, path):
if not self.abspath: if not self.abspath:
@ -595,13 +599,21 @@ class ReprTraceback(TerminalRepr):
class ReprEntry(TerminalRepr): class ReprEntry(TerminalRepr):
localssep = "_ " localssep = "_ "
def __init__(self, lines, reprfuncargs, reprlocals, filelocrepr): def __init__(self, lines, reprfuncargs, reprlocals, filelocrepr, short):
self.lines = lines self.lines = lines
self.reprfuncargs = reprfuncargs self.reprfuncargs = reprfuncargs
self.reprlocals = reprlocals self.reprlocals = reprlocals
self.reprfileloc = filelocrepr self.reprfileloc = filelocrepr
self.short = short
def toterminal(self, tw): 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: if self.reprfuncargs:
self.reprfuncargs.toterminal(tw) self.reprfuncargs.toterminal(tw)
for line in self.lines: for line in self.lines:

View File

@ -188,7 +188,11 @@ class CollectReport(BaseReport):
self.passed = True self.passed = True
self.result = result self.result = result
else: 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): if excinfo.errisinstance(py.test.skip.Exception):
self.skipped = True self.skipped = True
self.reason = str(excinfo.value) self.reason = str(excinfo.value)

View File

@ -172,14 +172,15 @@ class Node(object):
def _prunetraceback(self, traceback): def _prunetraceback(self, traceback):
return traceback return traceback
def _repr_failure_py(self, excinfo): def _repr_failure_py(self, excinfo, style=None):
excinfo.traceback = self._prunetraceback(excinfo.traceback) excinfo.traceback = self._prunetraceback(excinfo.traceback)
# XXX should excinfo.getrepr record all data and toterminal() # XXX should excinfo.getrepr record all data and toterminal()
# process it? # process it?
if self.config.option.tbstyle == "short": if style is None:
style = "short" if self.config.option.tbstyle == "short":
else: style = "short"
style = "long" else:
style = "long"
return excinfo.getrepr(funcargs=True, return excinfo.getrepr(funcargs=True,
showlocals=self.config.option.showlocals, showlocals=self.config.option.showlocals,
style=style) style=style)

View File

@ -253,7 +253,7 @@ class FunctionMixin(PyobjMixin):
traceback = ntraceback.filter() traceback = ntraceback.filter()
return traceback return traceback
def _repr_failure_py(self, excinfo): def _repr_failure_py(self, excinfo, style="long"):
if excinfo.errisinstance(funcargs.FuncargRequest.LookupError): if excinfo.errisinstance(funcargs.FuncargRequest.LookupError):
fspath, lineno, msg = self.reportinfo() fspath, lineno, msg = self.reportinfo()
lines, _ = inspect.getsourcelines(self.obj) lines, _ = inspect.getsourcelines(self.obj)
@ -261,11 +261,13 @@ class FunctionMixin(PyobjMixin):
if line.strip().startswith('def'): if line.strip().startswith('def'):
return FuncargLookupErrorRepr(fspath, lineno, return FuncargLookupErrorRepr(fspath, lineno,
lines[:i+1], str(excinfo.value)) 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): 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,
style=self.config.getvalue("tbstyle"))
shortfailurerepr = "F" shortfailurerepr = "F"

View File

@ -463,16 +463,18 @@ raise ValueError()
reprtb = p.repr_traceback_entry(excinfo.traceback[-2]) reprtb = p.repr_traceback_entry(excinfo.traceback[-2])
lines = reprtb.lines lines = reprtb.lines
basename = py.path.local(mod.__file__).basename basename = py.path.local(mod.__file__).basename
assert lines[0] == ' File "%s", line 5, in entry' % basename assert lines[0] == '> func1()'
assert lines[1] == ' func1()' assert basename in str(reprtb.reprfileloc.path)
assert reprtb.reprfileloc.lineno == 5
# test last entry # test last entry
p = FormattedExcinfo(style="short") p = FormattedExcinfo(style="short")
reprtb = p.repr_traceback_entry(excinfo.traceback[-1], excinfo) reprtb = p.repr_traceback_entry(excinfo.traceback[-1], excinfo)
lines = reprtb.lines lines = reprtb.lines
assert lines[0] == ' File "%s", line 3, in func1' % basename assert lines[0] == '> raise ValueError("hello")'
assert lines[1] == ' raise ValueError("hello")' assert lines[1] == 'E ValueError: hello'
assert lines[2] == 'E ValueError: hello' assert basename in str(reprtb.reprfileloc.path)
assert reprtb.reprfileloc.lineno == 3
def test_repr_tracebackentry_no(self, importasmod): def test_repr_tracebackentry_no(self, importasmod):
mod = importasmod(""" mod = importasmod("""
@ -525,12 +527,10 @@ raise ValueError()
last_lines = last_reprtb.lines last_lines = last_reprtb.lines
monkeypatch.undo() monkeypatch.undo()
basename = py.path.local(mod.__file__).basename basename = py.path.local(mod.__file__).basename
assert lines[0] == ' File "%s", line 5, in entry' % basename assert lines[0] == '> func1()'
assert lines[1] == ' func1()'
assert last_lines[0] == ' File "%s", line 3, in func1' % basename assert last_lines[0] == '> raise ValueError("hello")'
assert last_lines[1] == ' raise ValueError("hello")' assert last_lines[1] == 'E ValueError: hello'
assert last_lines[2] == 'E ValueError: hello'
def test_repr_traceback_and_excinfo(self, importasmod): def test_repr_traceback_and_excinfo(self, importasmod):
mod = importasmod(""" mod = importasmod("""

View File

@ -626,6 +626,35 @@ def test_terminalreporter_reportopt_conftestsetting(testdir):
"*1 passed*" "*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): def test_trace_reporting(testdir):
result = testdir.runpytest("--traceconfig") result = testdir.runpytest("--traceconfig")
result.stdout.fnmatch_lines([ result.stdout.fnmatch_lines([

View File

@ -484,3 +484,28 @@ class TestTracebackCutting:
assert out.find("conftest.py:2: ValueError") != -1 assert out.find("conftest.py:2: ValueError") != -1
numentries = out.count("_ _ _ _") # separator for traceback entries numentries = out.count("_ _ _ _") # separator for traceback entries
assert numentries >3 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*",
])