Improve tracebacks for ImportErrors in conftest.py files

Fix #3332
This commit is contained in:
Bruno Oliveira 2018-10-12 10:01:30 -03:00
parent 36dc671843
commit 8e11fe5304
3 changed files with 45 additions and 7 deletions

View File

@ -0,0 +1,4 @@
Improve the error displayed when a ``conftest.py`` file could not be imported.
In order to implement this, a new ``chain`` parameter was added to ``ExceptionInfo.getrepr``
to show or hide chained tracebacks in Python 3 (defaults to ``True``).

View File

@ -57,10 +57,24 @@ def main(args=None, plugins=None):
try: try:
config = _prepareconfig(args, plugins) config = _prepareconfig(args, plugins)
except ConftestImportFailure as e: except ConftestImportFailure as e:
from _pytest._code import ExceptionInfo
exc_info = ExceptionInfo(e.excinfo)
tw = py.io.TerminalWriter(sys.stderr) tw = py.io.TerminalWriter(sys.stderr)
for line in traceback.format_exception(*e.excinfo): tw.line(
"ImportError while loading conftest '{e.path}'.".format(e=e), red=True
)
from _pytest.python import filter_traceback
exc_info.traceback = exc_info.traceback.filter(filter_traceback)
exc_repr = (
exc_info.getrepr(style="short", chain=False)
if exc_info.traceback
else exc_info.exconly()
)
formatted_tb = safe_str(exc_repr)
for line in formatted_tb.splitlines():
tw.line(line.rstrip(), red=True) tw.line(line.rstrip(), red=True)
tw.line("ERROR: could not load %s\n" % (e.path,), red=True)
return 4 return 4
else: else:
try: try:

View File

@ -133,9 +133,16 @@ class TestGeneralUsage(object):
assert result.ret assert result.ret
result.stderr.fnmatch_lines(["*ERROR: not found:*{}".format(p2.basename)]) result.stderr.fnmatch_lines(["*ERROR: not found:*{}".format(p2.basename)])
def test_issue486_better_reporting_on_conftest_load_failure(self, testdir): def test_better_reporting_on_conftest_load_failure(self, testdir, request):
"""Show a user-friendly traceback on conftest import failures (#486, #3332)"""
testdir.makepyfile("") testdir.makepyfile("")
testdir.makeconftest("import qwerty") testdir.makeconftest(
"""
def foo():
import qwerty
foo()
"""
)
result = testdir.runpytest("--help") result = testdir.runpytest("--help")
result.stdout.fnmatch_lines( result.stdout.fnmatch_lines(
""" """
@ -144,10 +151,23 @@ class TestGeneralUsage(object):
""" """
) )
result = testdir.runpytest() result = testdir.runpytest()
dirname = request.node.name + "0"
exc_name = (
"ModuleNotFoundError" if sys.version_info >= (3, 6) else "ImportError"
)
result.stderr.fnmatch_lines( result.stderr.fnmatch_lines(
""" [
*ERROR*could not load*conftest.py* "ImportError while loading conftest '*{sep}{dirname}{sep}conftest.py'.".format(
""" dirname=dirname, sep=os.sep
),
"conftest.py:3: in <module>",
" foo()",
"conftest.py:2: in foo",
" import qwerty",
"E {}: No module named {q}qwerty{q}".format(
exc_name, q="'" if six.PY3 else ""
),
]
) )
def test_early_skip(self, testdir): def test_early_skip(self, testdir):