fix issue364: shorten and enhance tracebacks representation by default.
The new "--tb=auto" option (default) will only display long tracebacks for the first and last entry. You can get the old behaviour of printing all entries as long entries with "--tb=long". Also short entries by default are now printed very similarly to "--tb=native" ones.
This commit is contained in:
parent
76d5c9e4f4
commit
07e76cbef2
|
@ -1,6 +1,12 @@
|
||||||
NEXT (2.6)
|
NEXT (2.6)
|
||||||
-----------------------------------
|
-----------------------------------
|
||||||
|
|
||||||
|
- fix issue364: shorten and enhance tracebacks representation by default.
|
||||||
|
The new "--tb=auto" option (default) will only display long tracebacks
|
||||||
|
for the first and last entry. You can get the old behaviour of printing
|
||||||
|
all entries as long entries with "--tb=long". Also short entries by
|
||||||
|
default are now printed very similarly to "--tb=native" ones.
|
||||||
|
|
||||||
- fix issue514: teach assertion reinterpretation about private class attributes
|
- fix issue514: teach assertion reinterpretation about private class attributes
|
||||||
|
|
||||||
- change -v output to include full node IDs of tests. Users can copy
|
- change -v output to include full node IDs of tests. Users can copy
|
||||||
|
|
|
@ -1,2 +1,2 @@
|
||||||
#
|
#
|
||||||
__version__ = '2.6.0.dev1'
|
__version__ = '2.6.0.dev2'
|
||||||
|
|
|
@ -390,20 +390,24 @@ class Node(object):
|
||||||
fm = self.session._fixturemanager
|
fm = self.session._fixturemanager
|
||||||
if excinfo.errisinstance(fm.FixtureLookupError):
|
if excinfo.errisinstance(fm.FixtureLookupError):
|
||||||
return excinfo.value.formatrepr()
|
return excinfo.value.formatrepr()
|
||||||
|
tbfilter = True
|
||||||
if self.config.option.fulltrace:
|
if self.config.option.fulltrace:
|
||||||
style="long"
|
style="long"
|
||||||
else:
|
else:
|
||||||
self._prunetraceback(excinfo)
|
self._prunetraceback(excinfo)
|
||||||
# XXX should excinfo.getrepr record all data and toterminal()
|
tbfilter = False # prunetraceback already does it
|
||||||
# process it?
|
if style == "auto":
|
||||||
|
style = "long"
|
||||||
|
# XXX should excinfo.getrepr record all data and toterminal() process it?
|
||||||
if style is None:
|
if style is None:
|
||||||
if self.config.option.tbstyle == "short":
|
if self.config.option.tbstyle == "short":
|
||||||
style = "short"
|
style = "short"
|
||||||
else:
|
else:
|
||||||
style = "long"
|
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, tbfilter=tbfilter)
|
||||||
|
|
||||||
repr_failure = _repr_failure_py
|
repr_failure = _repr_failure_py
|
||||||
|
|
||||||
|
|
|
@ -578,6 +578,12 @@ class FunctionMixin(PyobjMixin):
|
||||||
if ntraceback == traceback:
|
if ntraceback == traceback:
|
||||||
ntraceback = ntraceback.cut(excludepath=cutdir)
|
ntraceback = ntraceback.cut(excludepath=cutdir)
|
||||||
excinfo.traceback = ntraceback.filter()
|
excinfo.traceback = ntraceback.filter()
|
||||||
|
# issue364: mark all but first and last frames to
|
||||||
|
# only show a single-line message for each frame
|
||||||
|
if self.config.option.tbstyle == "auto":
|
||||||
|
if len(excinfo.traceback) > 2:
|
||||||
|
for entry in excinfo.traceback[1:-1]:
|
||||||
|
entry.set_repr_style('short')
|
||||||
|
|
||||||
def _repr_failure_py(self, excinfo, style="long"):
|
def _repr_failure_py(self, excinfo, style="long"):
|
||||||
if excinfo.errisinstance(pytest.fail.Exception):
|
if excinfo.errisinstance(pytest.fail.Exception):
|
||||||
|
@ -588,8 +594,10 @@ class FunctionMixin(PyobjMixin):
|
||||||
|
|
||||||
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,
|
style = self.config.option.tbstyle
|
||||||
style=self.config.option.tbstyle)
|
if style == "auto":
|
||||||
|
style = "long"
|
||||||
|
return self._repr_failure_py(excinfo, style=style)
|
||||||
|
|
||||||
|
|
||||||
class Generator(FunctionMixin, PyCollector):
|
class Generator(FunctionMixin, PyCollector):
|
||||||
|
|
|
@ -23,8 +23,8 @@ def pytest_addoption(parser):
|
||||||
action="store", dest="report", default=None, metavar="opts",
|
action="store", dest="report", default=None, metavar="opts",
|
||||||
help="(deprecated, use -r)")
|
help="(deprecated, use -r)")
|
||||||
group._addoption('--tb', metavar="style",
|
group._addoption('--tb', metavar="style",
|
||||||
action="store", dest="tbstyle", default='long',
|
action="store", dest="tbstyle", default='auto',
|
||||||
choices=['long', 'short', 'no', 'line', 'native'],
|
choices=['auto', 'long', 'short', 'no', 'line', 'native'],
|
||||||
help="traceback print mode (long/short/line/native/no).")
|
help="traceback print mode (long/short/line/native/no).")
|
||||||
group._addoption('--fulltrace', '--full-trace',
|
group._addoption('--fulltrace', '--full-trace',
|
||||||
action="store_true", default=False,
|
action="store_true", default=False,
|
||||||
|
|
4
setup.py
4
setup.py
|
@ -17,7 +17,7 @@ long_description = open('README.rst').read()
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
install_requires = ['py>=1.4.20']
|
install_requires = ['py>=1.4.21.dev2']
|
||||||
if sys.version_info < (2, 7) or (3,) <= sys.version_info < (3, 2):
|
if sys.version_info < (2, 7) or (3,) <= sys.version_info < (3, 2):
|
||||||
install_requires.append('argparse')
|
install_requires.append('argparse')
|
||||||
if sys.platform == 'win32':
|
if sys.platform == 'win32':
|
||||||
|
@ -27,7 +27,7 @@ def main():
|
||||||
name='pytest',
|
name='pytest',
|
||||||
description='pytest: simple powerful testing with Python',
|
description='pytest: simple powerful testing with Python',
|
||||||
long_description=long_description,
|
long_description=long_description,
|
||||||
version='2.6.0.dev1',
|
version='2.6.0.dev2',
|
||||||
url='http://pytest.org',
|
url='http://pytest.org',
|
||||||
license='MIT license',
|
license='MIT license',
|
||||||
platforms=['unix', 'linux', 'osx', 'cygwin', 'win32'],
|
platforms=['unix', 'linux', 'osx', 'cygwin', 'win32'],
|
||||||
|
|
|
@ -641,7 +641,7 @@ class TestTracebackCutting:
|
||||||
assert "x = 1" not in out
|
assert "x = 1" not in out
|
||||||
assert "x = 2" not in out
|
assert "x = 2" not in out
|
||||||
result.stdout.fnmatch_lines([
|
result.stdout.fnmatch_lines([
|
||||||
">*asd*",
|
" *asd*",
|
||||||
"E*NameError*",
|
"E*NameError*",
|
||||||
])
|
])
|
||||||
result = testdir.runpytest("--fulltrace")
|
result = testdir.runpytest("--fulltrace")
|
||||||
|
|
|
@ -369,7 +369,7 @@ def test_traceback_failure(testdir):
|
||||||
def test_onefails():
|
def test_onefails():
|
||||||
f(3)
|
f(3)
|
||||||
""")
|
""")
|
||||||
result = testdir.runpytest(p1)
|
result = testdir.runpytest(p1, "--tb=long")
|
||||||
result.stdout.fnmatch_lines([
|
result.stdout.fnmatch_lines([
|
||||||
"*test_traceback_failure.py F",
|
"*test_traceback_failure.py F",
|
||||||
"====* FAILURES *====",
|
"====* FAILURES *====",
|
||||||
|
@ -389,6 +389,25 @@ def test_traceback_failure(testdir):
|
||||||
"*test_traceback_failure.py:4: AssertionError"
|
"*test_traceback_failure.py:4: AssertionError"
|
||||||
])
|
])
|
||||||
|
|
||||||
|
result = testdir.runpytest(p1) # "auto"
|
||||||
|
result.stdout.fnmatch_lines([
|
||||||
|
"*test_traceback_failure.py F",
|
||||||
|
"====* FAILURES *====",
|
||||||
|
"____*____",
|
||||||
|
"",
|
||||||
|
" def test_onefails():",
|
||||||
|
"> f(3)",
|
||||||
|
"",
|
||||||
|
"*test_*.py:6: ",
|
||||||
|
"",
|
||||||
|
" def f(x):",
|
||||||
|
"> assert x == g()",
|
||||||
|
"E assert 3 == 2",
|
||||||
|
"E + where 2 = g()",
|
||||||
|
"",
|
||||||
|
"*test_traceback_failure.py:4: AssertionError"
|
||||||
|
])
|
||||||
|
|
||||||
@pytest.mark.skipif("sys.version_info < (2,5) or '__pypy__' in sys.builtin_module_names or sys.platform.startswith('java')" )
|
@pytest.mark.skipif("sys.version_info < (2,5) or '__pypy__' in sys.builtin_module_names or sys.platform.startswith('java')" )
|
||||||
def test_warn_missing(testdir):
|
def test_warn_missing(testdir):
|
||||||
testdir.makepyfile("")
|
testdir.makepyfile("")
|
||||||
|
|
|
@ -559,8 +559,8 @@ def test_tbstyle_short(testdir):
|
||||||
assert 'x = 0' not in s
|
assert 'x = 0' not in s
|
||||||
result.stdout.fnmatch_lines([
|
result.stdout.fnmatch_lines([
|
||||||
"*%s:5*" % p.basename,
|
"*%s:5*" % p.basename,
|
||||||
">*assert x",
|
" assert x",
|
||||||
"E*assert*",
|
"E assert*",
|
||||||
])
|
])
|
||||||
result = testdir.runpytest()
|
result = testdir.runpytest()
|
||||||
s = result.stdout.str()
|
s = result.stdout.str()
|
||||||
|
@ -583,7 +583,7 @@ class TestGenericReporting:
|
||||||
testdir.makepyfile("import xyz\n")
|
testdir.makepyfile("import xyz\n")
|
||||||
result = testdir.runpytest(*option.args)
|
result = testdir.runpytest(*option.args)
|
||||||
result.stdout.fnmatch_lines([
|
result.stdout.fnmatch_lines([
|
||||||
"> import xyz",
|
"? import xyz",
|
||||||
"E ImportError: No module named *xyz*",
|
"E ImportError: No module named *xyz*",
|
||||||
"*1 error*",
|
"*1 error*",
|
||||||
])
|
])
|
||||||
|
|
Loading…
Reference in New Issue