[py-svn] py-trunk commit bb9c3511e28c: * improve and test --tb=short reporting
commits-noreply at bitbucket.org
commits-noreply at bitbucket.org
Sat May 22 16:16:05 CEST 2010
# HG changeset patch -- Bitbucket.org
# Project py-trunk
# URL http://bitbucket.org/hpk42/py-trunk/overview
# User holger krekel <holger at merlinux.eu>
# Date 1274537904 -7200
# Node ID bb9c3511e28c0b886df4ddc31573e105aea08cf4
# Parent 998d3278c0e1425e618d0ea22b681048acdb2252
* improve and test --tb=short reporting
* show --tb=short tracebacks for importing test modules
--- a/py/_plugin/pytest_runner.py
+++ b/py/_plugin/pytest_runner.py
@@ -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)
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -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
--- a/testing/plugin/test_pytest_terminal.py
+++ b/testing/plugin/test_pytest_terminal.py
@@ -626,6 +626,35 @@ def test_terminalreporter_reportopt_conf
"*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([
--- a/py/_code/code.py
+++ b/py/_code/code.py
@@ -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:
--- a/testing/test_pycollect.py
+++ b/testing/test_pycollect.py
@@ -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*",
+ ])
--- a/py/_test/pycollect.py
+++ b/py/_test/pycollect.py
@@ -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"
--- a/py/_test/collect.py
+++ b/py/_test/collect.py
@@ -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)
--- a/testing/code/test_excinfo.py
+++ b/testing/code/test_excinfo.py
@@ -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("""
More information about the pytest-commit
mailing list