Skip to content

Commit a1d446b

Browse files
committed
Display traceback from Import errors using pytest's short representation
Also omit pytest's own modules and internal libraries (py and pluggy) in low verbosity Fix #1976
1 parent 7d66e4e commit a1d446b

File tree

2 files changed

+31
-11
lines changed

2 files changed

+31
-11
lines changed

_pytest/python.py

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -22,11 +22,16 @@
2222
getlocation, enum,
2323
)
2424

25-
cutdir2 = py.path.local(_pytest.__file__).dirpath()
2625
cutdir1 = py.path.local(pluggy.__file__.rstrip("oc"))
26+
cutdir2 = py.path.local(_pytest.__file__).dirpath()
27+
cutdir3 = py.path.local(py.__file__).dirpath()
2728

2829

2930
def filter_traceback(entry):
31+
"""Return True if a TracebackEntry instance should be removed from tracebacks:
32+
* dynamically generated code (no code to show up for it);
33+
* internal traceback from pytest or its internal libraries, py and pluggy.
34+
"""
3035
# entry.path might sometimes return a str object when the entry
3136
# points to dynamically generated code
3237
# see https://bitbucket.org/pytest-dev/py/issues/71
@@ -37,7 +42,7 @@ def filter_traceback(entry):
3742
# entry.path might point to an inexisting file, in which case it will
3843
# alsso return a str object. see #1133
3944
p = py.path.local(entry.path)
40-
return p != cutdir1 and not p.relto(cutdir2)
45+
return p != cutdir1 and not p.relto(cutdir2) and not p.relto(cutdir3)
4146

4247

4348

@@ -424,14 +429,16 @@ def _importtestmodule(self):
424429
% e.args
425430
)
426431
except ImportError:
427-
import traceback
428-
stream = py.io.TextIO()
429-
traceback.print_exc(file=stream)
430-
formatted_tb = stream.getvalue()
432+
from _pytest._code.code import ExceptionInfo
433+
exc_info = ExceptionInfo()
434+
if self.config.getoption('verbose') < 2:
435+
exc_info.traceback = exc_info.traceback.filter(filter_traceback)
436+
exc_repr = exc_info.getrepr(style='short') if exc_info.traceback else exc_info.exconly()
437+
formatted_tb = py._builtin._totext(exc_repr)
431438
raise self.CollectError(
432439
"ImportError while importing test module '{fspath}'.\n"
433440
"Hint: make sure your test modules/packages have valid Python names.\n"
434-
"Original traceback:\n"
441+
"Traceback:\n"
435442
"{traceback}".format(fspath=self.fspath, traceback=formatted_tb)
436443
)
437444
except _pytest.runner.Skipped as e:

testing/python/collect.py

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
# -*- coding: utf-8 -*-
2+
import os
23
import sys
34
from textwrap import dedent
45

@@ -71,8 +72,12 @@ def test_invalid_test_module_name(self, testdir):
7172
"Hint: make sure your test modules/packages have valid Python names.",
7273
])
7374

74-
def test_show_full_traceback_import_error(self, testdir):
75-
"""Import errors when collecting modules should display the full traceback (#1976)."""
75+
@pytest.mark.parametrize('verbose', [0, 1, 2])
76+
def test_show_traceback_import_error(self, testdir, verbose):
77+
"""Import errors when collecting modules should display the traceback (#1976).
78+
79+
With low verbosity we omit pytest and internal modules, otherwise show all traceback entries.
80+
"""
7681
testdir.makepyfile(
7782
foo_traceback_import_error="""
7883
from bar_traceback_import_error import NOT_AVAILABLE
@@ -82,15 +87,23 @@ def test_show_full_traceback_import_error(self, testdir):
8287
testdir.makepyfile("""
8388
import foo_traceback_import_error
8489
""")
85-
result = testdir.runpytest()
90+
args = ('-v',) * verbose
91+
result = testdir.runpytest(*args)
8692
result.stdout.fnmatch_lines([
8793
"ImportError while importing test module*",
88-
"Original traceback:",
94+
"Traceback:",
8995
"*from bar_traceback_import_error import NOT_AVAILABLE",
9096
"*cannot import name *NOT_AVAILABLE*",
9197
])
9298
assert result.ret == 2
9399

100+
stdout = result.stdout.str()
101+
for name in ('_pytest', os.path.join('py', '_path')):
102+
if verbose == 2:
103+
assert name in stdout
104+
else:
105+
assert name not in stdout
106+
94107

95108
class TestClass:
96109
def test_class_with_init_warning(self, testdir):

0 commit comments

Comments
 (0)