From 5af866e4547f6b2b215ae43aa0612361fbbdd620 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 | 4 +++- testing/test_pluginmanager.py | 12 +++++++++--- 4 files changed, 30 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..cdcfd4ef18a 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,8 @@ def import_plugin(self, modname): try: __import__(importspec) except ImportError as e: - new_exc = ImportError('Error importing plugin "%s": %s' % (modname, e)) + msg = safe_str(e.args[0]) + new_exc = ImportError('Error importing plugin "%s": %s' % (modname, msg)) # 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..e61c84247c3 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(u'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))