Skip to content

#3332 improve traceback for import error #3345

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 6 commits into from
Closed

#3332 improve traceback for import error #3345

wants to merge 6 commits into from

Conversation

feuillemorte
Copy link
Contributor

Fixes #3332

Output:

ImportError while importing test module '/home/username/github/pytest-dev/pytest/pytest_3332/tests/conftest.py'.
Hint: make sure your test modules/packages have valid Python names.
Traceback:
  File "/home/username/github/pytest-dev/pytest/pytest_upstream/env/lib/python3.6/site-packages/pytest-3.5.1.dev11+g12a5f609-py3.6.egg/_pytest/assertion/rewrite.py", line 213, in load_module
    py.builtin.exec_(co, mod.__dict__)
  File "/home/username/github/pytest-dev/pytest/pytest_3332/tests/conftest.py", line 1, in <module>
    import wrong_import

E    ModuleNotFoundError: No module named 'wrong_import'

@feuillemorte feuillemorte added type: enhancement new feature or API change, should be merged into features branch topic: tracebacks related to displaying and handling of tracebacks labels Mar 27, 2018
tw.line("ERROR: could not load %s\n" % (e.path,), red=True)
formatted_tb = safe_str(e)
tw.line(
"ImportError while importing test module '{path}'.\n"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"test module" sounds a bit confusing here, I think "conftest module" would fit better here.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed

@@ -36,7 +36,7 @@ def __str__(self):
etype, evalue, etb = self.excinfo
formatted = traceback.format_tb(etb)
# The level of the tracebacks we want to print is hand crafted :(
return repr(evalue) + '\n' + ''.join(formatted[2:])
return ''.join(formatted[2:]) + '\nE ' + etype.__name__ + ': ' + str(evalue)
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What do you think about this line? Is it good? Now it looks like

E    ModuleNotFoundError: No module named 'wrong_import'

@coveralls
Copy link

coveralls commented Mar 27, 2018

Coverage Status

Coverage increased (+0.08%) to 92.885% when pulling acbf49a on feuillemorte:3332-improve-traceback-for-import-error into ed118d7 on pytest-dev:features.

@nicoddemus
Copy link
Member

Hi @feuillemorte, thanks for another PR!

Your change fixes the problem for root conftest.py files, but not for conftest.py files in subdirectories. For example:

# content of root/sub/conftest.py
import invalid_module

# content of root/sub/test_foo.py
def test(): pass

When ran from the root directory, this produces:

======================================================= ERRORS ========================================================
__________________________________________________ ERROR collecting  __________________________________________________
_pytest\config.py:340: in _getconftestmodules
    return self._path2confmods[path]
E   KeyError: local('C:\\pytest\\.tmp\\conftest-error\\sub')

During handling of the above exception, another exception occurred:
_pytest\config.py:371: in _importconftest
    return self._conftestpath2mod[conftestpath]
E   KeyError: local('C:\\pytest\\.tmp\\conftest-error\\sub\\conftest.py')

During handling of the above exception, another exception occurred:
_pytest\config.py:377: in _importconftest
    mod = conftestpath.pyimport()
.env36\lib\site-packages\py\_path\local.py:668: in pyimport
    __import__(modname)
_pytest\assertion\rewrite.py:213: in load_module
    py.builtin.exec_(co, mod.__dict__)
.tmp\conftest-error\sub\conftest.py:3: in <module>
    import invalid_module
E   ModuleNotFoundError: No module named 'invalid_module'

During handling of the above exception, another exception occurred:
.env36\lib\site-packages\py\_path\common.py:377: in visit
    for x in Visitor(fil, rec, ignore, bf, sort).gen(self):
.env36\lib\site-packages\py\_path\common.py:418: in gen
    dirs = self.optsort([p for p in entries
.env36\lib\site-packages\py\_path\common.py:419: in <listcomp>
    if p.check(dir=1) and (rec is None or rec(p))])
_pytest\main.py:434: in _recurse
    ihook = self.gethookproxy(path)
_pytest\main.py:338: in gethookproxy
    my_conftestmodules = pm._getconftestmodules(fspath)
_pytest\config.py:354: in _getconftestmodules
    mod = self._importconftest(conftestpath)
_pytest\config.py:382: in _importconftest
    raise ConftestImportFailure(conftestpath, sys.exc_info())
E   _pytest.config.ConftestImportFailure:   File "c:\pytest\_pytest\assertion\rewrite.py", line 213, in load_module
E       py.builtin.exec_(co, mod.__dict__)
E     File "C:\pytest\.tmp\conftest-error\sub\conftest.py", line 3, in <module>
E       import invalid_module
E
E   E    ModuleNotFoundError: No module named 'invalid_module'
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! Interrupted: 1 errors during collection !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
=============================================== 1 error in 0.21 seconds ===============================================

I hacked the code a bit and produced the following patch:

diff --git a/_pytest/config.py b/_pytest/config.py
index 7a4622a..5927435 100644
--- a/_pytest/config.py
+++ b/_pytest/config.py
@@ -209,6 +209,7 @@ class PytestPluginManager(PluginManager):
         self.rewrite_hook = _pytest.assertion.DummyRewriteHook()
         # Used to know when we are importing conftests after the pytest_configure stage
         self._configured = False
+        self._config = None

     def addhooks(self, module_or_class):
         """
@@ -285,6 +286,7 @@ class PytestPluginManager(PluginManager):
                                 "trylast: mark a hook implementation function such that the "
                                 "plugin machinery will try to call it last/as late as possible.")
         self._configured = True
+        self._config = config

     def _warn(self, message):
         kwargs = message if isinstance(message, dict) else {
@@ -351,7 +353,18 @@ class PytestPluginManager(PluginManager):
                         continue
                     conftestpath = parent.join("conftest.py")
                     if conftestpath.isfile():
-                        mod = self._importconftest(conftestpath)
+                        try:
+                            mod = self._importconftest(conftestpath)
+                        except ConftestImportFailure as e:
+                            from _pytest.nodes import Collector
+                            from _pytest._code.code import ExceptionInfo
+                            from _pytest.python import filter_traceback
+                            exc_info = ExceptionInfo(e.excinfo)
+                            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 = safe_str(exc_repr)
+                            raise Collector.CollectError(formatted_tb)
                         clist.append(mod)

             self._path2confmods[path] = clist

This produces a better output:

______________________________ ERROR collecting  ______________________________
_pytest\config.py:342: in _getconftestmodules
    return self._path2confmods[path]
E   KeyError: local('C:\\pytest\\.tmp\\conftest-error\\sub')

During handling of the above exception, another exception occurred:
_pytest\config.py:384: in _importconftest
    return self._conftestpath2mod[conftestpath]
E   KeyError: local('C:\\pytest\\.tmp\\conftest-error\\sub\\conftest.py')

During handling of the above exception, another exception occurred:
.tmp\conftest-error\sub\conftest.py:3: in <module>
    import invalid_module
E   ModuleNotFoundError: No module named 'invalid_module'
!!!!!!!!!!!!!!!!!!! Interrupted: 1 errors during collection !!!!!!!!!!!!!!!!!!!
=========================== 1 error in 0.12 seconds ===========================

But I think it can still be improved, we need to investigate a bit more how to get rid of the chained exception traceback entries.

Please note that my handling in the except ConftestImportFailure: block is a hack, we should move the code which formats the exception in a shorter message to a function and reuse that in config.py:main and in

pytest/_pytest/python.py

Lines 427 to 439 in ff3d13e

except ImportError:
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 = safe_str(exc_repr)
raise self.CollectError(
"ImportError while importing test module '{fspath}'.\n"
"Hint: make sure your test modules/packages have valid Python names.\n"
"Traceback:\n"
"{traceback}".format(fspath=self.fspath, traceback=formatted_tb)
)
.

@feuillemorte
Copy link
Contributor Author

@nicoddemus
test failed

tox -e py36 testing/acceptance_test.py::TestGeneralUsage::test_issue109_sibling_conftests_not_loaded
    def test_issue109_sibling_conftests_not_loaded(self, testdir):
        sub1 = testdir.mkdir("sub1")
        sub2 = testdir.mkdir("sub2")
        sub1.join("conftest.py").write("assert 0")
        result = testdir.runpytest(sub2)
        assert result.ret == EXIT_NOTESTSCOLLECTED
        sub2.ensure("__init__.py")
        p = sub2.ensure("test_hello.py")
        result = testdir.runpytest(p)
        assert result.ret == EXIT_NOTESTSCOLLECTED
        result = testdir.runpytest(sub1)
>       assert result.ret == EXIT_USAGEERROR
E       assert 3 == 4

what is ret? What means '3'?

Traceback is very huge in this test:

Traceback (most recent call last):
  File "/home/feuillemorte/github/pytest-dev/pytest/pytest_3332/.tox/py36/lib/python3.6/site-packages/_pytest/pytester.py", line 780, in runpytest_inprocess
    reprec = self.inline_run(*args, **kwargs)
  File "/home/feuillemorte/github/pytest-dev/pytest/pytest_3332/.tox/py36/lib/python3.6/site-packages/_pytest/pytester.py", line 749, in inline_run
    ret = pytest.main(list(args), plugins=plugins)
  File "/home/feuillemorte/github/pytest-dev/pytest/pytest_3332/.tox/py36/lib/python3.6/site-packages/_pytest/config.py", line 52, in main
    config = _prepareconfig(args, plugins)
  File "/home/feuillemorte/github/pytest-dev/pytest/pytest_3332/.tox/py36/lib/python3.6/site-packages/_pytest/config.py", line 169, in _prepareconfig
    pluginmanager=pluginmanager, args=args)
  File "/home/feuillemorte/github/pytest-dev/pytest/pytest_3332/.tox/py36/lib/python3.6/site-packages/pluggy/__init__.py", line 617, in __call__
    return self._hookexec(self, self._nonwrappers + self._wrappers, kwargs)
  File "/home/feuillemorte/github/pytest-dev/pytest/pytest_3332/.tox/py36/lib/python3.6/site-packages/pluggy/__init__.py", line 222, in _hookexec
    return self._inner_hookexec(hook, methods, kwargs)
  File "/home/feuillemorte/github/pytest-dev/pytest/pytest_3332/.tox/py36/lib/python3.6/site-packages/pluggy/__init__.py", line 216, in <lambda>
    firstresult=hook.spec_opts.get('firstresult'),
  File "/home/feuillemorte/github/pytest-dev/pytest/pytest_3332/.tox/py36/lib/python3.6/site-packages/pluggy/callers.py", line 196, in _multicall
    gen.send(outcome)
  File "/home/feuillemorte/github/pytest-dev/pytest/pytest_3332/.tox/py36/lib/python3.6/site-packages/_pytest/helpconfig.py", line 68, in pytest_cmdline_parse
    config = outcome.get_result()
  File "/home/feuillemorte/github/pytest-dev/pytest/pytest_3332/.tox/py36/lib/python3.6/site-packages/pluggy/callers.py", line 76, in get_result
    raise ex[1].with_traceback(ex[2])
  File "/home/feuillemorte/github/pytest-dev/pytest/pytest_3332/.tox/py36/lib/python3.6/site-packages/pluggy/callers.py", line 180, in _multicall
    res = hook_impl.function(*args)
  File "/home/feuillemorte/github/pytest-dev/pytest/pytest_3332/.tox/py36/lib/python3.6/site-packages/_pytest/config.py", line 974, in pytest_cmdline_parse
    self.parse(args)
  File "/home/feuillemorte/github/pytest-dev/pytest/pytest_3332/.tox/py36/lib/python3.6/site-packages/_pytest/config.py", line 1124, in parse
    self._preparse(args, addopts=addopts)
  File "/home/feuillemorte/github/pytest-dev/pytest/pytest_3332/.tox/py36/lib/python3.6/site-packages/_pytest/config.py", line 1095, in _preparse
    args=args, parser=self._parser)
  File "/home/feuillemorte/github/pytest-dev/pytest/pytest_3332/.tox/py36/lib/python3.6/site-packages/pluggy/__init__.py", line 617, in __call__
    return self._hookexec(self, self._nonwrappers + self._wrappers, kwargs)
  File "/home/feuillemorte/github/pytest-dev/pytest/pytest_3332/.tox/py36/lib/python3.6/site-packages/pluggy/__init__.py", line 222, in _hookexec
    return self._inner_hookexec(hook, methods, kwargs)
  File "/home/feuillemorte/github/pytest-dev/pytest/pytest_3332/.tox/py36/lib/python3.6/site-packages/pluggy/__init__.py", line 216, in <lambda>
    firstresult=hook.spec_opts.get('firstresult'),
  File "/home/feuillemorte/github/pytest-dev/pytest/pytest_3332/.tox/py36/lib/python3.6/site-packages/pluggy/callers.py", line 201, in _multicall
    return outcome.get_result()
  File "/home/feuillemorte/github/pytest-dev/pytest/pytest_3332/.tox/py36/lib/python3.6/site-packages/pluggy/callers.py", line 76, in get_result
    raise ex[1].with_traceback(ex[2])
  File "/home/feuillemorte/github/pytest-dev/pytest/pytest_3332/.tox/py36/lib/python3.6/site-packages/pluggy/callers.py", line 180, in _multicall
    res = hook_impl.function(*args)
  File "/home/feuillemorte/github/pytest-dev/pytest/pytest_3332/.tox/py36/lib/python3.6/site-packages/_pytest/config.py", line 1020, in pytest_load_initial_conftests
    self.pluginmanager._set_initial_conftests(early_config.known_args_namespace)
  File "/home/feuillemorte/github/pytest-dev/pytest/pytest_3332/.tox/py36/lib/python3.6/site-packages/_pytest/config.py", line 348, in _set_initial_conftests
    self._try_load_conftest(anchor)
  File "/home/feuillemorte/github/pytest-dev/pytest/pytest_3332/.tox/py36/lib/python3.6/site-packages/_pytest/config.py", line 354, in _try_load_conftest
    self._getconftestmodules(anchor)
  File "/home/feuillemorte/github/pytest-dev/pytest/pytest_3332/.tox/py36/lib/python3.6/site-packages/_pytest/config.py", line 386, in _getconftestmodules
    print_short_traceback(e, self._config)
  File "/home/feuillemorte/github/pytest-dev/pytest/pytest_3332/.tox/py36/lib/python3.6/site-packages/_pytest/config.py", line 195, in print_short_traceback
    ) from None
_pytest.nodes.Collector.CollectError: ImportError while importing test module '/tmp/pytest-of-feuillemorte/pytest-27/test_issue109_sibling_conftests_not_loaded0/sub1/conftest.py'.
Hint: make sure your test modules/packages have valid Python names.
Traceback:
/home/feuillemorte/github/pytest-dev/pytest/pytest_3332/.tox/py36/lib/python3.6/site-packages/_pytest/config.py:365: in _getconftestmodules
    return self._path2confmods[path]
E   KeyError: local('/tmp/pytest-of-feuillemorte/pytest-27/test_issue109_sibling_conftests_not_loaded0/sub1')

During handling of the above exception, another exception occurred:
/home/feuillemorte/github/pytest-dev/pytest/pytest_3332/.tox/py36/lib/python3.6/site-packages/_pytest/config.py:403: in _importconftest
    return self._conftestpath2mod[conftestpath]
E   KeyError: local('/tmp/pytest-of-feuillemorte/pytest-27/test_issue109_sibling_conftests_not_loaded0/sub1/conftest.py')

During handling of the above exception, another exception occurred:
/home/feuillemorte/github/pytest-dev/pytest/pytest_3332/.tox/py36/lib/python3.6/site-packages/_pytest/config.py:409: in _importconftest
    mod = conftestpath.pyimport()
/home/feuillemorte/github/pytest-dev/pytest/pytest_3332/.tox/py36/lib/python3.6/site-packages/py/_path/local.py:668: in pyimport
    __import__(modname)
<frozen importlib._bootstrap>:971: in _find_and_load
    ???
<frozen importlib._bootstrap>:955: in _find_and_load_unlocked
    ???
<frozen importlib._bootstrap>:656: in _load_unlocked
    ???
<frozen importlib._bootstrap>:626: in _load_backward_compatible
    ???
/home/feuillemorte/github/pytest-dev/pytest/pytest_3332/.tox/py36/lib/python3.6/site-packages/_pytest/assertion/rewrite.py:213: in load_module
    py.builtin.exec_(co, mod.__dict__)
sub1/conftest.py:1: in <module>
    assert 0
E   AssertionError: assert 0

During handling of the above exception, another exception occurred:
/home/feuillemorte/github/pytest-dev/pytest/pytest_3332/.tox/py36/lib/python3.6/site-packages/_pytest/config.py:383: in _getconftestmodules
    mod = self._importconftest(conftestpath)
/home/feuillemorte/github/pytest-dev/pytest/pytest_3332/.tox/py36/lib/python3.6/site-packages/_pytest/config.py:414: in _importconftest
    raise ConftestImportFailure(conftestpath, sys.exc_info())
E   _pytest.config.ConftestImportFailure:   File "<frozen importlib._bootstrap>", line 971, in _find_and_load
E     File "<frozen importlib._bootstrap>", line 955, in _find_and_load_unlocked
E     File "<frozen importlib._bootstrap>", line 656, in _load_unlocked
E     File "<frozen importlib._bootstrap>", line 626, in _load_backward_compatible
E     File "/home/feuillemorte/github/pytest-dev/pytest/pytest_3332/.tox/py36/lib/python3.6/site-packages/_pytest/assertion/rewrite.py", line 213, in load_module
E       py.builtin.exec_(co, mod.__dict__)
E     File "/tmp/pytest-of-feuillemorte/pytest-27/test_issue109_sibling_conftests_not_loaded0/sub1/conftest.py", line 1, in <module>
E       assert 0
E   
E   E    AssertionError: assert 0

if I only raise Collector.CollectError() without message I get this traceback:

Traceback (most recent call last):
  File "/home/feuillemorte/github/pytest-dev/pytest/pytest_upstream/env/lib/python3.6/site-packages/pytest-3.5.1.dev12+gacbf49a3.d20180403-py3.6.egg/_pytest/config.py", line 366, in _getconftestmodules
    return self._path2confmods[path]
KeyError: local('/home/feuillemorte/github/pytest-dev/pytest/pytest_3332/tests/test_api')

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/home/feuillemorte/github/pytest-dev/pytest/pytest_upstream/env/lib/python3.6/site-packages/pytest-3.5.1.dev12+gacbf49a3.d20180403-py3.6.egg/_pytest/config.py", line 404, in _importconftest
    return self._conftestpath2mod[conftestpath]
KeyError: local('/home/feuillemorte/github/pytest-dev/pytest/pytest_3332/tests/test_api/conftest.py')

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/home/feuillemorte/github/pytest-dev/pytest/pytest_upstream/env/lib/python3.6/site-packages/pytest-3.5.1.dev12+gacbf49a3.d20180403-py3.6.egg/_pytest/config.py", line 410, in _importconftest
    mod = conftestpath.pyimport()
  File "/home/feuillemorte/github/pytest-dev/pytest/pytest_upstream/env/lib/python3.6/site-packages/py-1.5.2-py3.6.egg/py/_path/local.py", line 668, in pyimport
    __import__(modname)
  File "<frozen importlib._bootstrap>", line 971, in _find_and_load
  File "<frozen importlib._bootstrap>", line 955, in _find_and_load_unlocked
  File "<frozen importlib._bootstrap>", line 656, in _load_unlocked
  File "<frozen importlib._bootstrap>", line 626, in _load_backward_compatible
  File "/home/feuillemorte/github/pytest-dev/pytest/pytest_upstream/env/lib/python3.6/site-packages/pytest-3.5.1.dev12+gacbf49a3.d20180403-py3.6.egg/_pytest/assertion/rewrite.py", line 213, in load_module
    py.builtin.exec_(co, mod.__dict__)
  File "/home/feuillemorte/github/pytest-dev/pytest/pytest_3332/tests/test_api/conftest.py", line 2, in <module>
    assert 0
AssertionError: assert 0

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/home/feuillemorte/github/pytest-dev/pytest/pytest_upstream/env/lib/python3.6/site-packages/pytest-3.5.1.dev12+gacbf49a3.d20180403-py3.6.egg/_pytest/config.py", line 384, in _getconftestmodules
    mod = self._importconftest(conftestpath)
  File "/home/feuillemorte/github/pytest-dev/pytest/pytest_upstream/env/lib/python3.6/site-packages/pytest-3.5.1.dev12+gacbf49a3.d20180403-py3.6.egg/_pytest/config.py", line 415, in _importconftest
    raise ConftestImportFailure(conftestpath, sys.exc_info())
_pytest.config.ConftestImportFailure:   File "<frozen importlib._bootstrap>", line 971, in _find_and_load
  File "<frozen importlib._bootstrap>", line 955, in _find_and_load_unlocked
  File "<frozen importlib._bootstrap>", line 656, in _load_unlocked
  File "<frozen importlib._bootstrap>", line 626, in _load_backward_compatible
  File "/home/feuillemorte/github/pytest-dev/pytest/pytest_upstream/env/lib/python3.6/site-packages/pytest-3.5.1.dev12+gacbf49a3.d20180403-py3.6.egg/_pytest/assertion/rewrite.py", line 213, in load_module
    py.builtin.exec_(co, mod.__dict__)
  File "/home/feuillemorte/github/pytest-dev/pytest/pytest_3332/tests/test_api/conftest.py", line 2, in <module>
    assert 0

E    AssertionError: assert 0

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/home/feuillemorte/github/pytest-dev/pytest/pytest_upstream/env/bin/pytest", line 11, in <module>
    load_entry_point('pytest==3.5.1.dev12+gacbf49a3.d20180403', 'console_scripts', 'pytest')()
  File "/home/feuillemorte/github/pytest-dev/pytest/pytest_upstream/env/lib/python3.6/site-packages/pytest-3.5.1.dev12+gacbf49a3.d20180403-py3.6.egg/_pytest/config.py", line 52, in main
    config = _prepareconfig(args, plugins)
  File "/home/feuillemorte/github/pytest-dev/pytest/pytest_upstream/env/lib/python3.6/site-packages/pytest-3.5.1.dev12+gacbf49a3.d20180403-py3.6.egg/_pytest/config.py", line 169, in _prepareconfig
    pluginmanager=pluginmanager, args=args)
  File "/home/feuillemorte/github/pytest-dev/pytest/pytest_upstream/env/lib/python3.6/site-packages/pluggy-0.6.0-py3.6.egg/pluggy/__init__.py", line 617, in __call__
    return self._hookexec(self, self._nonwrappers + self._wrappers, kwargs)
  File "/home/feuillemorte/github/pytest-dev/pytest/pytest_upstream/env/lib/python3.6/site-packages/pluggy-0.6.0-py3.6.egg/pluggy/__init__.py", line 222, in _hookexec
    return self._inner_hookexec(hook, methods, kwargs)
  File "/home/feuillemorte/github/pytest-dev/pytest/pytest_upstream/env/lib/python3.6/site-packages/pluggy-0.6.0-py3.6.egg/pluggy/__init__.py", line 216, in <lambda>
    firstresult=hook.spec_opts.get('firstresult'),
  File "/home/feuillemorte/github/pytest-dev/pytest/pytest_upstream/env/lib/python3.6/site-packages/pluggy-0.6.0-py3.6.egg/pluggy/callers.py", line 196, in _multicall
    gen.send(outcome)
  File "/home/feuillemorte/github/pytest-dev/pytest/pytest_upstream/env/lib/python3.6/site-packages/pytest-3.5.1.dev12+gacbf49a3.d20180403-py3.6.egg/_pytest/helpconfig.py", line 68, in pytest_cmdline_parse
    config = outcome.get_result()
  File "/home/feuillemorte/github/pytest-dev/pytest/pytest_upstream/env/lib/python3.6/site-packages/pluggy-0.6.0-py3.6.egg/pluggy/callers.py", line 76, in get_result
    raise ex[1].with_traceback(ex[2])
  File "/home/feuillemorte/github/pytest-dev/pytest/pytest_upstream/env/lib/python3.6/site-packages/pluggy-0.6.0-py3.6.egg/pluggy/callers.py", line 180, in _multicall
    res = hook_impl.function(*args)
  File "/home/feuillemorte/github/pytest-dev/pytest/pytest_upstream/env/lib/python3.6/site-packages/pytest-3.5.1.dev12+gacbf49a3.d20180403-py3.6.egg/_pytest/config.py", line 975, in pytest_cmdline_parse
    self.parse(args)
  File "/home/feuillemorte/github/pytest-dev/pytest/pytest_upstream/env/lib/python3.6/site-packages/pytest-3.5.1.dev12+gacbf49a3.d20180403-py3.6.egg/_pytest/config.py", line 1125, in parse
    self._preparse(args, addopts=addopts)
  File "/home/feuillemorte/github/pytest-dev/pytest/pytest_upstream/env/lib/python3.6/site-packages/pytest-3.5.1.dev12+gacbf49a3.d20180403-py3.6.egg/_pytest/config.py", line 1096, in _preparse
    args=args, parser=self._parser)
  File "/home/feuillemorte/github/pytest-dev/pytest/pytest_upstream/env/lib/python3.6/site-packages/pluggy-0.6.0-py3.6.egg/pluggy/__init__.py", line 617, in __call__
    return self._hookexec(self, self._nonwrappers + self._wrappers, kwargs)
  File "/home/feuillemorte/github/pytest-dev/pytest/pytest_upstream/env/lib/python3.6/site-packages/pluggy-0.6.0-py3.6.egg/pluggy/__init__.py", line 222, in _hookexec
    return self._inner_hookexec(hook, methods, kwargs)
  File "/home/feuillemorte/github/pytest-dev/pytest/pytest_upstream/env/lib/python3.6/site-packages/pluggy-0.6.0-py3.6.egg/pluggy/__init__.py", line 216, in <lambda>
    firstresult=hook.spec_opts.get('firstresult'),
  File "/home/feuillemorte/github/pytest-dev/pytest/pytest_upstream/env/lib/python3.6/site-packages/pluggy-0.6.0-py3.6.egg/pluggy/callers.py", line 201, in _multicall
    return outcome.get_result()
  File "/home/feuillemorte/github/pytest-dev/pytest/pytest_upstream/env/lib/python3.6/site-packages/pluggy-0.6.0-py3.6.egg/pluggy/callers.py", line 76, in get_result
    raise ex[1].with_traceback(ex[2])
  File "/home/feuillemorte/github/pytest-dev/pytest/pytest_upstream/env/lib/python3.6/site-packages/pluggy-0.6.0-py3.6.egg/pluggy/callers.py", line 180, in _multicall
    res = hook_impl.function(*args)
  File "/home/feuillemorte/github/pytest-dev/pytest/pytest_upstream/env/lib/python3.6/site-packages/pytest-3.5.1.dev12+gacbf49a3.d20180403-py3.6.egg/_pytest/config.py", line 1021, in pytest_load_initial_conftests
    self.pluginmanager._set_initial_conftests(early_config.known_args_namespace)
  File "/home/feuillemorte/github/pytest-dev/pytest/pytest_upstream/env/lib/python3.6/site-packages/pytest-3.5.1.dev12+gacbf49a3.d20180403-py3.6.egg/_pytest/config.py", line 349, in _set_initial_conftests
    self._try_load_conftest(anchor)
  File "/home/feuillemorte/github/pytest-dev/pytest/pytest_upstream/env/lib/python3.6/site-packages/pytest-3.5.1.dev12+gacbf49a3.d20180403-py3.6.egg/_pytest/config.py", line 360, in _try_load_conftest
    self._getconftestmodules(x)
  File "/home/feuillemorte/github/pytest-dev/pytest/pytest_upstream/env/lib/python3.6/site-packages/pytest-3.5.1.dev12+gacbf49a3.d20180403-py3.6.egg/_pytest/config.py", line 387, in _getconftestmodules
    print_short_traceback(e, self._config)
  File "/home/feuillemorte/github/pytest-dev/pytest/pytest_upstream/env/lib/python3.6/site-packages/pytest-3.5.1.dev12+gacbf49a3.d20180403-py3.6.egg/_pytest/config.py", line 189, in print_short_traceback
    raise Collector.CollectError()
_pytest.nodes.CollectError

I can use raise from None construction:

    raise Collector.CollectError(
        "ImportError while importing test module '{fspath}'.\n"
        "Hint: make sure your test modules/packages have valid Python names.\n"
        "Traceback:\n"
        "{traceback}".format(fspath=error.path, traceback=formatted_tb)
    ) from None

But in this case we have 2 tracebacks:

Traceback (most recent call last):
  File "/home/feuillemorte/github/pytest-dev/pytest/pytest_upstream/env/bin/pytest", line 11, in <module>
    load_entry_point('pytest==3.5.1.dev12+gacbf49a3.d20180403', 'console_scripts', 'pytest')()
  File "/home/feuillemorte/github/pytest-dev/pytest/pytest_upstream/env/lib/python3.6/site-packages/pytest-3.5.1.dev12+gacbf49a3.d20180403-py3.6.egg/_pytest/config.py", line 52, in main
    config = _prepareconfig(args, plugins)
  File "/home/feuillemorte/github/pytest-dev/pytest/pytest_upstream/env/lib/python3.6/site-packages/pytest-3.5.1.dev12+gacbf49a3.d20180403-py3.6.egg/_pytest/config.py", line 169, in _prepareconfig
    pluginmanager=pluginmanager, args=args)
  File "/home/feuillemorte/github/pytest-dev/pytest/pytest_upstream/env/lib/python3.6/site-packages/pluggy-0.6.0-py3.6.egg/pluggy/__init__.py", line 617, in __call__
    return self._hookexec(self, self._nonwrappers + self._wrappers, kwargs)
  File "/home/feuillemorte/github/pytest-dev/pytest/pytest_upstream/env/lib/python3.6/site-packages/pluggy-0.6.0-py3.6.egg/pluggy/__init__.py", line 222, in _hookexec
    return self._inner_hookexec(hook, methods, kwargs)
  File "/home/feuillemorte/github/pytest-dev/pytest/pytest_upstream/env/lib/python3.6/site-packages/pluggy-0.6.0-py3.6.egg/pluggy/__init__.py", line 216, in <lambda>
    firstresult=hook.spec_opts.get('firstresult'),
  File "/home/feuillemorte/github/pytest-dev/pytest/pytest_upstream/env/lib/python3.6/site-packages/pluggy-0.6.0-py3.6.egg/pluggy/callers.py", line 196, in _multicall
    gen.send(outcome)
  File "/home/feuillemorte/github/pytest-dev/pytest/pytest_upstream/env/lib/python3.6/site-packages/pytest-3.5.1.dev12+gacbf49a3.d20180403-py3.6.egg/_pytest/helpconfig.py", line 68, in pytest_cmdline_parse
    config = outcome.get_result()
  File "/home/feuillemorte/github/pytest-dev/pytest/pytest_upstream/env/lib/python3.6/site-packages/pluggy-0.6.0-py3.6.egg/pluggy/callers.py", line 76, in get_result
    raise ex[1].with_traceback(ex[2])
  File "/home/feuillemorte/github/pytest-dev/pytest/pytest_upstream/env/lib/python3.6/site-packages/pluggy-0.6.0-py3.6.egg/pluggy/callers.py", line 180, in _multicall
    res = hook_impl.function(*args)
  File "/home/feuillemorte/github/pytest-dev/pytest/pytest_upstream/env/lib/python3.6/site-packages/pytest-3.5.1.dev12+gacbf49a3.d20180403-py3.6.egg/_pytest/config.py", line 975, in pytest_cmdline_parse
    self.parse(args)
  File "/home/feuillemorte/github/pytest-dev/pytest/pytest_upstream/env/lib/python3.6/site-packages/pytest-3.5.1.dev12+gacbf49a3.d20180403-py3.6.egg/_pytest/config.py", line 1125, in parse
    self._preparse(args, addopts=addopts)
  File "/home/feuillemorte/github/pytest-dev/pytest/pytest_upstream/env/lib/python3.6/site-packages/pytest-3.5.1.dev12+gacbf49a3.d20180403-py3.6.egg/_pytest/config.py", line 1096, in _preparse
    args=args, parser=self._parser)
  File "/home/feuillemorte/github/pytest-dev/pytest/pytest_upstream/env/lib/python3.6/site-packages/pluggy-0.6.0-py3.6.egg/pluggy/__init__.py", line 617, in __call__
    return self._hookexec(self, self._nonwrappers + self._wrappers, kwargs)
  File "/home/feuillemorte/github/pytest-dev/pytest/pytest_upstream/env/lib/python3.6/site-packages/pluggy-0.6.0-py3.6.egg/pluggy/__init__.py", line 222, in _hookexec
    return self._inner_hookexec(hook, methods, kwargs)
  File "/home/feuillemorte/github/pytest-dev/pytest/pytest_upstream/env/lib/python3.6/site-packages/pluggy-0.6.0-py3.6.egg/pluggy/__init__.py", line 216, in <lambda>
    firstresult=hook.spec_opts.get('firstresult'),
  File "/home/feuillemorte/github/pytest-dev/pytest/pytest_upstream/env/lib/python3.6/site-packages/pluggy-0.6.0-py3.6.egg/pluggy/callers.py", line 201, in _multicall
    return outcome.get_result()
  File "/home/feuillemorte/github/pytest-dev/pytest/pytest_upstream/env/lib/python3.6/site-packages/pluggy-0.6.0-py3.6.egg/pluggy/callers.py", line 76, in get_result
    raise ex[1].with_traceback(ex[2])
  File "/home/feuillemorte/github/pytest-dev/pytest/pytest_upstream/env/lib/python3.6/site-packages/pluggy-0.6.0-py3.6.egg/pluggy/callers.py", line 180, in _multicall
    res = hook_impl.function(*args)
  File "/home/feuillemorte/github/pytest-dev/pytest/pytest_upstream/env/lib/python3.6/site-packages/pytest-3.5.1.dev12+gacbf49a3.d20180403-py3.6.egg/_pytest/config.py", line 1021, in pytest_load_initial_conftests
    self.pluginmanager._set_initial_conftests(early_config.known_args_namespace)
  File "/home/feuillemorte/github/pytest-dev/pytest/pytest_upstream/env/lib/python3.6/site-packages/pytest-3.5.1.dev12+gacbf49a3.d20180403-py3.6.egg/_pytest/config.py", line 349, in _set_initial_conftests
    self._try_load_conftest(anchor)
  File "/home/feuillemorte/github/pytest-dev/pytest/pytest_upstream/env/lib/python3.6/site-packages/pytest-3.5.1.dev12+gacbf49a3.d20180403-py3.6.egg/_pytest/config.py", line 360, in _try_load_conftest
    self._getconftestmodules(x)
  File "/home/feuillemorte/github/pytest-dev/pytest/pytest_upstream/env/lib/python3.6/site-packages/pytest-3.5.1.dev12+gacbf49a3.d20180403-py3.6.egg/_pytest/config.py", line 387, in _getconftestmodules
    print_short_traceback(e, self._config)
  File "/home/feuillemorte/github/pytest-dev/pytest/pytest_upstream/env/lib/python3.6/site-packages/pytest-3.5.1.dev12+gacbf49a3.d20180403-py3.6.egg/_pytest/config.py", line 196, in print_short_traceback
    ) from None
_pytest.nodes.CollectError: ImportError while importing test module '/home/feuillemorte/github/pytest-dev/pytest/pytest_3332/tests/test_api/conftest.py'.
Hint: make sure your test modules/packages have valid Python names.
Traceback:
../pytest_upstream/env/lib/python3.6/site-packages/pytest-3.5.1.dev12+gacbf49a3.d20180403-py3.6.egg/_pytest/config.py:366: in _getconftestmodules
    return self._path2confmods[path]
E   KeyError: local('/home/feuillemorte/github/pytest-dev/pytest/pytest_3332/tests/test_api')

During handling of the above exception, another exception occurred:
../pytest_upstream/env/lib/python3.6/site-packages/pytest-3.5.1.dev12+gacbf49a3.d20180403-py3.6.egg/_pytest/config.py:404: in _importconftest
    return self._conftestpath2mod[conftestpath]
E   KeyError: local('/home/feuillemorte/github/pytest-dev/pytest/pytest_3332/tests/test_api/conftest.py')

During handling of the above exception, another exception occurred:
../pytest_upstream/env/lib/python3.6/site-packages/pytest-3.5.1.dev12+gacbf49a3.d20180403-py3.6.egg/_pytest/config.py:410: in _importconftest
    mod = conftestpath.pyimport()
../pytest_upstream/env/lib/python3.6/site-packages/py-1.5.2-py3.6.egg/py/_path/local.py:668: in pyimport
    __import__(modname)
<frozen importlib._bootstrap>:971: in _find_and_load
    ???
<frozen importlib._bootstrap>:955: in _find_and_load_unlocked
    ???
<frozen importlib._bootstrap>:656: in _load_unlocked
    ???
<frozen importlib._bootstrap>:626: in _load_backward_compatible
    ???
../pytest_upstream/env/lib/python3.6/site-packages/pytest-3.5.1.dev12+gacbf49a3.d20180403-py3.6.egg/_pytest/assertion/rewrite.py:213: in load_module
    py.builtin.exec_(co, mod.__dict__)
tests/test_api/conftest.py:2: in <module>
    assert 0
E   AssertionError: assert 0

During handling of the above exception, another exception occurred:
../pytest_upstream/env/lib/python3.6/site-packages/pytest-3.5.1.dev12+gacbf49a3.d20180403-py3.6.egg/_pytest/config.py:384: in _getconftestmodules
    mod = self._importconftest(conftestpath)
../pytest_upstream/env/lib/python3.6/site-packages/pytest-3.5.1.dev12+gacbf49a3.d20180403-py3.6.egg/_pytest/config.py:415: in _importconftest
    raise ConftestImportFailure(conftestpath, sys.exc_info())
E   _pytest.config.ConftestImportFailure:   File "<frozen importlib._bootstrap>", line 971, in _find_and_load
E     File "<frozen importlib._bootstrap>", line 955, in _find_and_load_unlocked
E     File "<frozen importlib._bootstrap>", line 656, in _load_unlocked
E     File "<frozen importlib._bootstrap>", line 626, in _load_backward_compatible
E     File "/home/feuillemorte/github/pytest-dev/pytest/pytest_upstream/env/lib/python3.6/site-packages/pytest-3.5.1.dev12+gacbf49a3.d20180403-py3.6.egg/_pytest/assertion/rewrite.py", line 213, in load_module
E       py.builtin.exec_(co, mod.__dict__)
E     File "/home/feuillemorte/github/pytest-dev/pytest/pytest_3332/tests/test_api/conftest.py", line 2, in <module>
E       assert 0
E   
E   E    AssertionError: assert 0

It's also huge

@nicoddemus
Copy link
Member

Hi @feuillemorte, I'm just passing by so I don't have much time to look deeper into the code, but to answer your question 3 is one of the possible error codes:

pytest/_pytest/main.py

Lines 21 to 27 in ad0b433

# exitcodes for the command line
EXIT_OK = 0
EXIT_TESTSFAILED = 1
EXIT_INTERRUPTED = 2
EXIT_INTERNALERROR = 3
EXIT_USAGEERROR = 4
EXIT_NOTESTSCOLLECTED = 5

I'm surprised that raise Collector.CollectError(...) from None is generating a traceback so big, seemed to me as the right way to go.

Is this code in a branch?

I will take a look as soon as I have some time, although this week is turning busier than I expected. 😅

@feuillemorte
Copy link
Contributor Author

feuillemorte commented Apr 4, 2018

@nicoddemus I pushed some code, but some tests will be failed.
Try to look into a code too.

@feuillemorte
Copy link
Contributor Author

feuillemorte commented Apr 11, 2018

Do we really need config.getoption('verbose') here?

    exc_info = ExceptionInfo()
    if config and 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 = safe_str(exc_repr)

If we remove it and write smth like

def print_short_traceback(error):
    from _pytest.nodes import Collector
    formatted_tb = safe_str(error)
    sys.tracebacklimit = 0
    raise Collector.CollectError(
        "ImportError while importing test module '{fspath}'.\n"
        "Hint: make sure your test modules/packages have valid Python names.\n"
        "Traceback:\n"
        "{traceback}".format(fspath=error.path, traceback=formatted_tb)
    ) from None

We get beautiful traceback :)

Traceback (most recent call last):
_pytest.nodes.Collector.CollectError: ImportError while importing test module '/tmp/pytest-of-feuillemorte/pytest-63/test_issue109_sibling_conftests_not_loaded0/sub1/conftest.py'.
Hint: make sure your test modules/packages have valid Python names.
Traceback:
  File "<frozen importlib._bootstrap>", line 971, in _find_and_load
  File "<frozen importlib._bootstrap>", line 955, in _find_and_load_unlocked
  File "<frozen importlib._bootstrap>", line 656, in _load_unlocked
  File "<frozen importlib._bootstrap>", line 626, in _load_backward_compatible
  File "/home/feuillemorte/github/pytest-dev/pytest/pytest_3332/_pytest/assertion/rewrite.py", line 213, in load_module
    py.builtin.exec_(co, mod.__dict__)
  File "/tmp/pytest-of-feuillemorte/pytest-63/test_issue109_sibling_conftests_not_loaded0/sub1/conftest.py", line 1, in <module>
    assert 0

E    AssertionError: assert 0

@nicoddemus
Copy link
Member

Do we really need config.getoption('verbose') here?

We get beautiful traceback :)

I kinda disagree, I would prefer a traceback which shows only the error in the conftest.py file, namely this:

File "/tmp/pytest-of-feuillemorte/pytest-63/test_issue109_sibling_conftests_not_loaded0/sub1/conftest.py", line 1, in <module>
    assert 0

Also it is not a good idea to change sys.tracebacklimit = 0 I think because we would be changing a global and never restoring it.

Btw sorry I didn't have time to take a look at your PR yet. Thanks for the patience. 😁

@nicoddemus
Copy link
Member

@feuillemorte thanks a lot for all the work here, but I believe this will be supplanted by #4077! 👍 🙇

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
topic: tracebacks related to displaying and handling of tracebacks type: enhancement new feature or API change, should be merged into features branch
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants