diff --git a/CHANGELOG b/CHANGELOG index 52bf9e503..a29cc9022 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,6 +1,12 @@ 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 - change -v output to include full node IDs of tests. Users can copy diff --git a/_pytest/__init__.py b/_pytest/__init__.py index a1a02b7bf..0b348cdc7 100644 --- a/_pytest/__init__.py +++ b/_pytest/__init__.py @@ -1,2 +1,2 @@ # -__version__ = '2.6.0.dev1' +__version__ = '2.6.0.dev2' diff --git a/_pytest/main.py b/_pytest/main.py index 7b61d8114..ecd854ee0 100644 --- a/_pytest/main.py +++ b/_pytest/main.py @@ -390,20 +390,24 @@ class Node(object): fm = self.session._fixturemanager if excinfo.errisinstance(fm.FixtureLookupError): return excinfo.value.formatrepr() + tbfilter = True if self.config.option.fulltrace: style="long" else: self._prunetraceback(excinfo) - # XXX should excinfo.getrepr record all data and toterminal() - # process it? + tbfilter = False # prunetraceback already does it + if style == "auto": + style = "long" + # XXX should excinfo.getrepr record all data and toterminal() process it? 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) + style=style, tbfilter=tbfilter) repr_failure = _repr_failure_py diff --git a/_pytest/python.py b/_pytest/python.py index 01925f539..cba0f1643 100644 --- a/_pytest/python.py +++ b/_pytest/python.py @@ -578,6 +578,12 @@ class FunctionMixin(PyobjMixin): if ntraceback == traceback: ntraceback = ntraceback.cut(excludepath=cutdir) 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"): if excinfo.errisinstance(pytest.fail.Exception): @@ -588,8 +594,10 @@ class FunctionMixin(PyobjMixin): def repr_failure(self, excinfo, outerr=None): 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): diff --git a/_pytest/terminal.py b/_pytest/terminal.py index 0e0a53463..abb8f1549 100644 --- a/_pytest/terminal.py +++ b/_pytest/terminal.py @@ -23,8 +23,8 @@ def pytest_addoption(parser): action="store", dest="report", default=None, metavar="opts", help="(deprecated, use -r)") group._addoption('--tb', metavar="style", - action="store", dest="tbstyle", default='long', - choices=['long', 'short', 'no', 'line', 'native'], + action="store", dest="tbstyle", default='auto', + choices=['auto', 'long', 'short', 'no', 'line', 'native'], help="traceback print mode (long/short/line/native/no).") group._addoption('--fulltrace', '--full-trace', action="store_true", default=False, diff --git a/setup.py b/setup.py index 616376d53..86ff67fde 100644 --- a/setup.py +++ b/setup.py @@ -17,7 +17,7 @@ long_description = open('README.rst').read() 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): install_requires.append('argparse') if sys.platform == 'win32': @@ -27,7 +27,7 @@ def main(): name='pytest', description='pytest: simple powerful testing with Python', long_description=long_description, - version='2.6.0.dev1', + version='2.6.0.dev2', url='http://pytest.org', license='MIT license', platforms=['unix', 'linux', 'osx', 'cygwin', 'win32'], diff --git a/testing/python/collect.py b/testing/python/collect.py index e0fb5aaf7..8acd40ffa 100644 --- a/testing/python/collect.py +++ b/testing/python/collect.py @@ -641,7 +641,7 @@ class TestTracebackCutting: assert "x = 1" not in out assert "x = 2" not in out result.stdout.fnmatch_lines([ - ">*asd*", + " *asd*", "E*NameError*", ]) result = testdir.runpytest("--fulltrace") diff --git a/testing/test_assertion.py b/testing/test_assertion.py index aae722cf1..5b873cdc2 100644 --- a/testing/test_assertion.py +++ b/testing/test_assertion.py @@ -369,7 +369,7 @@ def test_traceback_failure(testdir): def test_onefails(): f(3) """) - result = testdir.runpytest(p1) + result = testdir.runpytest(p1, "--tb=long") result.stdout.fnmatch_lines([ "*test_traceback_failure.py F", "====* FAILURES *====", @@ -389,6 +389,25 @@ def test_traceback_failure(testdir): "*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')" ) def test_warn_missing(testdir): testdir.makepyfile("") diff --git a/testing/test_terminal.py b/testing/test_terminal.py index d9ff347f2..202e08e0a 100644 --- a/testing/test_terminal.py +++ b/testing/test_terminal.py @@ -559,8 +559,8 @@ def test_tbstyle_short(testdir): assert 'x = 0' not in s result.stdout.fnmatch_lines([ "*%s:5*" % p.basename, - ">*assert x", - "E*assert*", + " assert x", + "E assert*", ]) result = testdir.runpytest() s = result.stdout.str() @@ -583,7 +583,7 @@ class TestGenericReporting: testdir.makepyfile("import xyz\n") result = testdir.runpytest(*option.args) result.stdout.fnmatch_lines([ - "> import xyz", + "? import xyz", "E ImportError: No module named *xyz*", "*1 error*", ])