diff --git a/src/_pytest/config/__init__.py b/src/_pytest/config/__init__.py index d45feb624..a4fc6e7c1 100644 --- a/src/_pytest/config/__init__.py +++ b/src/_pytest/config/__init__.py @@ -88,10 +88,15 @@ class ExitCode(enum.IntEnum): class ConftestImportFailure(Exception): def __init__(self, path, excinfo): - Exception.__init__(self, path, excinfo) + super().__init__(path, excinfo) self.path = path self.excinfo = excinfo # type: Tuple[Type[Exception], Exception, TracebackType] + def __str__(self): + return "{}: {} (from {})".format( + self.excinfo[0].__name__, self.excinfo[1], self.path + ) + def main(args=None, plugins=None) -> Union[int, ExitCode]: """ return exit code, after performing an in-process test run. diff --git a/testing/test_config.py b/testing/test_config.py index f6bf0499f..7d553e63b 100644 --- a/testing/test_config.py +++ b/testing/test_config.py @@ -10,6 +10,7 @@ import pytest from _pytest.compat import importlib_metadata from _pytest.config import _iter_rewritable_modules from _pytest.config import Config +from _pytest.config import ConftestImportFailure from _pytest.config import ExitCode from _pytest.config.exceptions import UsageError from _pytest.config.findpaths import determine_setup @@ -1471,3 +1472,19 @@ class TestPytestPluginsVariable: assert res.ret == 0 msg = "Defining 'pytest_plugins' in a non-top-level conftest is no longer supported" assert msg not in res.stdout.str() + + +def test_conftest_import_error_repr(tmpdir): + """ + ConftestImportFailure should use a short error message and readable path to the failed + conftest.py file + """ + path = tmpdir.join("foo/conftest.py") + with pytest.raises( + ConftestImportFailure, + match=re.escape("RuntimeError: some error (from {})".format(path)), + ): + try: + raise RuntimeError("some error") + except Exception: + raise ConftestImportFailure(path, sys.exc_info())