Skip to content

Commit

Permalink
Display traceback from Import errors using pytest's short representation
Browse files Browse the repository at this point in the history
Also omit pytest's own modules and internal libraries (py and pluggy) in low verbosity

Fix #1976
  • Loading branch information
nicoddemus committed Oct 4, 2016
1 parent 7d66e4e commit a1d446b
Show file tree
Hide file tree
Showing 2 changed files with 31 additions and 11 deletions.
21 changes: 14 additions & 7 deletions _pytest/python.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,16 @@
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
Expand All @@ -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)



Expand Down Expand Up @@ -424,14 +429,16 @@ def _importtestmodule(self):
% 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:
Expand Down
21 changes: 17 additions & 4 deletions testing/python/collect.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
# -*- coding: utf-8 -*-
import os
import sys
from textwrap import dedent

Expand Down Expand Up @@ -71,8 +72,12 @@ def test_invalid_test_module_name(self, testdir):
"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
Expand All @@ -82,15 +87,23 @@ def test_show_full_traceback_import_error(self, testdir):
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):
Expand Down

0 comments on commit a1d446b

Please sign in to comment.