diff --git a/_pytest/python.py b/_pytest/python.py index 74bb4de43..62d2896ea 100644 --- a/_pytest/python.py +++ b/_pytest/python.py @@ -22,11 +22,16 @@ from _pytest.compat import ( getlocation, enum, ) -cutdir2 = py.path.local(_pytest.__file__).dirpath() cutdir1 = py.path.local(pluggy.__file__.rstrip("oc")) +cutdir2 = py.path.local(_pytest.__file__).dirpath() +cutdir3 = py.path.local(py.__file__).dirpath() def filter_traceback(entry): + """Return True if a TracebackEntry instance should be removed from tracebacks: + * dynamically generated code (no code to show up for it); + * internal traceback from pytest or its internal libraries, py and pluggy. + """ # entry.path might sometimes return a str object when the entry # points to dynamically generated code # see https://bitbucket.org/pytest-dev/py/issues/71 @@ -37,7 +42,7 @@ def filter_traceback(entry): # entry.path might point to an inexisting file, in which case it will # alsso return a str object. see #1133 p = py.path.local(entry.path) - return p != cutdir1 and not p.relto(cutdir2) + return p != cutdir1 and not p.relto(cutdir2) and not p.relto(cutdir3) @@ -424,14 +429,16 @@ class Module(pytest.File, PyCollector): % e.args ) except ImportError: - import traceback - stream = py.io.TextIO() - traceback.print_exc(file=stream) - formatted_tb = stream.getvalue() + from _pytest._code.code import ExceptionInfo + exc_info = ExceptionInfo() + if self.config.getoption('verbose') < 2: + exc_info.traceback = exc_info.traceback.filter(filter_traceback) + exc_repr = exc_info.getrepr(style='short') if exc_info.traceback else exc_info.exconly() + formatted_tb = py._builtin._totext(exc_repr) raise self.CollectError( "ImportError while importing test module '{fspath}'.\n" "Hint: make sure your test modules/packages have valid Python names.\n" - "Original traceback:\n" + "Traceback:\n" "{traceback}".format(fspath=self.fspath, traceback=formatted_tb) ) except _pytest.runner.Skipped as e: diff --git a/testing/python/collect.py b/testing/python/collect.py index 04ce53a54..2913b11a4 100644 --- a/testing/python/collect.py +++ b/testing/python/collect.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- +import os import sys from textwrap import dedent @@ -71,8 +72,12 @@ class TestModule: "Hint: make sure your test modules/packages have valid Python names.", ]) - def test_show_full_traceback_import_error(self, testdir): - """Import errors when collecting modules should display the full traceback (#1976).""" + @pytest.mark.parametrize('verbose', [0, 1, 2]) + def test_show_traceback_import_error(self, testdir, verbose): + """Import errors when collecting modules should display the traceback (#1976). + + With low verbosity we omit pytest and internal modules, otherwise show all traceback entries. + """ testdir.makepyfile( foo_traceback_import_error=""" from bar_traceback_import_error import NOT_AVAILABLE @@ -82,15 +87,23 @@ class TestModule: testdir.makepyfile(""" import foo_traceback_import_error """) - result = testdir.runpytest() + args = ('-v',) * verbose + result = testdir.runpytest(*args) result.stdout.fnmatch_lines([ "ImportError while importing test module*", - "Original traceback:", + "Traceback:", "*from bar_traceback_import_error import NOT_AVAILABLE", "*cannot import name *NOT_AVAILABLE*", ]) assert result.ret == 2 + stdout = result.stdout.str() + for name in ('_pytest', os.path.join('py', '_path')): + if verbose == 2: + assert name in stdout + else: + assert name not in stdout + class TestClass: def test_class_with_init_warning(self, testdir):