From 19aab6365ff75861762150834ced49dabb3de6c2 Mon Sep 17 00:00:00 2001
From: feuillemorte <fmorte@ya.ru>
Date: Tue, 27 Mar 2018 13:29:08 +0300
Subject: [PATCH 1/5] #3332 Improved tracebacks for ImportErrors in conftest

---
 _pytest/config.py | 14 ++++++++++----
 1 file changed, 10 insertions(+), 4 deletions(-)

diff --git a/_pytest/config.py b/_pytest/config.py
index eb9c2a1f25f..ab42c8d64a1 100644
--- a/_pytest/config.py
+++ b/_pytest/config.py
@@ -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)
 
 
 def main(args=None, plugins=None):
@@ -52,9 +52,15 @@ def main(args=None, plugins=None):
             config = _prepareconfig(args, plugins)
         except ConftestImportFailure as e:
             tw = py.io.TerminalWriter(sys.stderr)
-            for line in traceback.format_exception(*e.excinfo):
-                tw.line(line.rstrip(), red=True)
-            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"
+                "Hint: make sure your test modules/packages have valid Python names.\n"
+                "Traceback:\n"
+                "{traceback}".format(path=e.path, traceback=formatted_tb),
+                red=True
+            )
+
             return 4
         else:
             try:

From 9b5b9e38a9d74608a7effe5eb72dbb5abcdf1e1d Mon Sep 17 00:00:00 2001
From: feuillemorte <fmorte@ya.ru>
Date: Tue, 27 Mar 2018 13:29:59 +0300
Subject: [PATCH 2/5] #3332 Fix tests for new traceback

---
 testing/acceptance_test.py | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/testing/acceptance_test.py b/testing/acceptance_test.py
index 89a44911f27..9e919caafd6 100644
--- a/testing/acceptance_test.py
+++ b/testing/acceptance_test.py
@@ -145,9 +145,9 @@ def test_issue486_better_reporting_on_conftest_load_failure(self, testdir):
             *warning*conftest.py*
         """)
         result = testdir.runpytest()
-        result.stderr.fnmatch_lines("""
-            *ERROR*could not load*conftest.py*
-        """)
+        result.stderr.fnmatch_lines(["*ImportError while importing test module*conftest.py*",
+                                    "E    *Error: No module named*qwerty*"]
+                                    )
 
     def test_early_skip(self, testdir):
         testdir.mkdir("xyz")

From fdb5c254e77fae9e7825732518ff156528a9d0c1 Mon Sep 17 00:00:00 2001
From: feuillemorte <fmorte@ya.ru>
Date: Tue, 27 Mar 2018 13:30:21 +0300
Subject: [PATCH 3/5] #3332 Added changelog file

---
 changelog/3332.trivial | 1 +
 1 file changed, 1 insertion(+)
 create mode 100644 changelog/3332.trivial

diff --git a/changelog/3332.trivial b/changelog/3332.trivial
new file mode 100644
index 00000000000..d5886ffd4b1
--- /dev/null
+++ b/changelog/3332.trivial
@@ -0,0 +1 @@
+Improved tracebacks for ImportErrors in conftest.py

From acbf49a367337796dbda5dbec703faa19863b383 Mon Sep 17 00:00:00 2001
From: feuillemorte <fmorte@ya.ru>
Date: Tue, 27 Mar 2018 13:49:09 +0300
Subject: [PATCH 4/5] #3332 Fix message

---
 _pytest/config.py          | 2 +-
 testing/acceptance_test.py | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/_pytest/config.py b/_pytest/config.py
index ab42c8d64a1..7a4622ae88c 100644
--- a/_pytest/config.py
+++ b/_pytest/config.py
@@ -54,7 +54,7 @@ def main(args=None, plugins=None):
             tw = py.io.TerminalWriter(sys.stderr)
             formatted_tb = safe_str(e)
             tw.line(
-                "ImportError while importing test module '{path}'.\n"
+                "ImportError while importing conftest module '{path}'.\n"
                 "Hint: make sure your test modules/packages have valid Python names.\n"
                 "Traceback:\n"
                 "{traceback}".format(path=e.path, traceback=formatted_tb),
diff --git a/testing/acceptance_test.py b/testing/acceptance_test.py
index 9e919caafd6..29c941ca6de 100644
--- a/testing/acceptance_test.py
+++ b/testing/acceptance_test.py
@@ -145,7 +145,7 @@ def test_issue486_better_reporting_on_conftest_load_failure(self, testdir):
             *warning*conftest.py*
         """)
         result = testdir.runpytest()
-        result.stderr.fnmatch_lines(["*ImportError while importing test module*conftest.py*",
+        result.stderr.fnmatch_lines(["*ImportError while importing conftest module*conftest.py*",
                                     "E    *Error: No module named*qwerty*"]
                                     )
 

From 75497b17e66c3e8ede5bd41c8d98bdd8cd1e345d Mon Sep 17 00:00:00 2001
From: feuillemorte <fmorte@ya.ru>
Date: Wed, 4 Apr 2018 13:18:20 +0300
Subject: [PATCH 5/5] #3332 Added traceback function

---
 _pytest/config.py | 25 ++++++++++++++++++++++++-
 _pytest/python.py | 19 ++++---------------
 2 files changed, 28 insertions(+), 16 deletions(-)

diff --git a/_pytest/config.py b/_pytest/config.py
index 7a4622ae88c..947e6200082 100644
--- a/_pytest/config.py
+++ b/_pytest/config.py
@@ -172,6 +172,23 @@ def _prepareconfig(args=None, plugins=None):
         raise
 
 
+def print_short_traceback(error, config):
+    from _pytest.nodes import Collector
+    from _pytest._code.code import ExceptionInfo
+    from _pytest.python import filter_traceback
+    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)
+    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
+
+
 class PytestPluginManager(PluginManager):
     """
     Overwrites :py:class:`pluggy.PluginManager <pluggy.PluginManager>` to add pytest-specific
@@ -209,6 +226,7 @@ def __init__(self):
         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 +303,7 @@ def pytest_configure(self, config):
                                 "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 +370,11 @@ def _getconftestmodules(self, path):
                         continue
                     conftestpath = parent.join("conftest.py")
                     if conftestpath.isfile():
-                        mod = self._importconftest(conftestpath)
+                        mod = None
+                        try:
+                            mod = self._importconftest(conftestpath)
+                        except ConftestImportFailure as e:
+                            print_short_traceback(e, self._config)
                         clist.append(mod)
 
             self._path2confmods[path] = clist
diff --git a/_pytest/python.py b/_pytest/python.py
index f9f17afd794..bcc9eedf9df 100644
--- a/_pytest/python.py
+++ b/_pytest/python.py
@@ -14,7 +14,7 @@
 import py
 import six
 from _pytest.mark import MarkerError
-from _pytest.config import hookimpl
+from _pytest.config import hookimpl, print_short_traceback
 
 import _pytest
 import pluggy
@@ -25,7 +25,7 @@
     isclass, isfunction, is_generator, ascii_escaped,
     REGEX_TYPE, STRING_TYPES, NoneType, NOTSET,
     get_real_func, getfslineno, safe_getattr,
-    safe_str, getlocation, enum,
+    getlocation, enum,
 )
 from _pytest.outcomes import fail
 from _pytest.mark.structures import transfer_markers
@@ -424,19 +424,8 @@ def _importtestmodule(self):
                 "unique basename for your test file modules"
                 % e.args
             )
-        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)
-            )
+        except ImportError as e:
+            print_short_traceback(e, self.config)
         except _pytest.runner.Skipped as e:
             if e.allow_module_level:
                 raise
