From 08836f10af725cbc7ae150f2c4e9bdc1162ad1e5 Mon Sep 17 00:00:00 2001 From: Bruno Oliveira Date: Wed, 12 Oct 2016 17:46:47 -0300 Subject: [PATCH] Handle import errors with non-ascii messages when importing plugins Fix #1998 --- CHANGELOG.rst | 4 +++- _pytest/compat.py | 16 +++++++++++++++- _pytest/config.py | 3 ++- testing/test_pluginmanager.py | 12 +++++++++--- 4 files changed, 29 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 96a257781bc..c03e58a4cda 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -6,7 +6,8 @@ * Import errors when collecting test modules now display the full traceback (`#1976`_). Thanks `@cwitty`_ for the report and `@nicoddemus`_ for the PR. -* +* When loading plugins, import errors which contain non-ascii messages are now properly handled in Python 2 (`#1998`_). + Thanks `@nicoddemus`_ for the PR. * @@ -14,6 +15,7 @@ .. _@cwitty: https://github.com/cwitty .. _#1976: https://github.com/pytest-dev/pytest/issues/1976 +.. _#1998: https://github.com/pytest-dev/pytest/issues/1998 diff --git a/_pytest/compat.py b/_pytest/compat.py index 1d8c2f331e3..51fc3bc5c1b 100644 --- a/_pytest/compat.py +++ b/_pytest/compat.py @@ -213,4 +213,18 @@ def _is_unittest_unexpected_success_a_failure(): Changed in version 3.4: Returns False if there were any unexpectedSuccesses from tests marked with the expectedFailure() decorator. """ - return sys.version_info >= (3, 4) \ No newline at end of file + return sys.version_info >= (3, 4) + + +if _PY3: + def safe_str(v): + """returns v as string""" + return str(v) +else: + def safe_str(v): + """returns v as string, converting to ascii if necessary""" + try: + return str(v) + except UnicodeError: + errors = 'replace' + return v.encode('ascii', errors) diff --git a/_pytest/config.py b/_pytest/config.py index 661a8513daa..4b08241a623 100644 --- a/_pytest/config.py +++ b/_pytest/config.py @@ -12,6 +12,7 @@ import _pytest.hookspec # the extension point definitions import _pytest.assertion from _pytest._pluggy import PluginManager, HookimplMarker, HookspecMarker +from _pytest.compat import safe_str hookimpl = HookimplMarker("pytest") hookspec = HookspecMarker("pytest") @@ -405,7 +406,7 @@ def import_plugin(self, modname): try: __import__(importspec) except ImportError as e: - new_exc = ImportError('Error importing plugin "%s": %s' % (modname, e)) + new_exc = ImportError('Error importing plugin "%s": %s' % (modname, safe_str(e))) # copy over name and path attributes for attr in ('name', 'path'): if hasattr(e, attr): diff --git a/testing/test_pluginmanager.py b/testing/test_pluginmanager.py index 36847638d48..a966adb2bea 100644 --- a/testing/test_pluginmanager.py +++ b/testing/test_pluginmanager.py @@ -1,3 +1,4 @@ +# encoding: UTF-8 import pytest import py import os @@ -179,15 +180,20 @@ def test_default_markers(testdir): ]) -def test_importplugin_issue375(testdir, pytestpm): +def test_importplugin_error_message(testdir, pytestpm): """Don't hide import errors when importing plugins and provide an easy to debug message. + + See #375 and #1998. """ testdir.syspathinsert(testdir.tmpdir) - testdir.makepyfile(qwe="import aaaa") + testdir.makepyfile(qwe=""" + # encoding: UTF-8 + raise ImportError('Not possible to import: ☺') + """) with pytest.raises(ImportError) as excinfo: pytestpm.import_plugin("qwe") - expected = '.*Error importing plugin "qwe": No module named \'?aaaa\'?' + expected = '.*Error importing plugin "qwe": Not possible to import: .' assert py.std.re.match(expected, str(excinfo.value))