Make monkeypatch differentiate ImportError sources

Previously `monkeypatch` assumed that any `ImportError` was caused by
a mistake in the specified import path. However this assumption is false 
in case the import target itself causes an `ImportError`.

Fixes: #900
This commit is contained in:
Ulrich Petri 2015-12-27 22:42:42 +01:00
parent 522224ee7c
commit 8d49abb0d1
2 changed files with 38 additions and 1 deletions

View File

@ -1,8 +1,14 @@
""" monkeypatching and mocking functionality. """
import os, sys
import re
from py.builtin import _basestring
RE_IMPORT_ERROR_NAME = re.compile("^No module named (.*)$")
def pytest_funcarg__monkeypatch(request):
"""The returned ``monkeypatch`` funcarg provides these
helper methods to modify objects, dictionaries or os.environ::
@ -34,14 +40,28 @@ def derive_importpath(import_path, raising):
(import_path,))
rest = []
target = import_path
target_parts = set(target.split("."))
while target:
try:
obj = __import__(target, None, None, "__doc__")
except ImportError:
except ImportError as ex:
if hasattr(ex, 'name'):
# Python >= 3.3
failed_name = ex.name
else:
match = RE_IMPORT_ERROR_NAME.match(ex.args[0])
assert match
failed_name = match.group(1)
if "." not in target:
__tracebackhide__ = True
pytest.fail("could not import any sub part: %s" %
import_path)
elif failed_name != target \
and not any(p == failed_name for p in target_parts):
# target is importable but causes ImportError itself
__tracebackhide__ = True
pytest.fail("import error in %s: %s" % (target, ex.args[0]))
target, name = target.rsplit(".", 1)
rest.append(name)
else:

View File

@ -1,4 +1,6 @@
import os, sys
import textwrap
import pytest
from _pytest.monkeypatch import monkeypatch as MonkeyPatch
@ -245,6 +247,21 @@ def test_issue185_time_breaks(testdir):
*1 passed*
""")
def test_importerror(testdir):
p = testdir.mkpydir("package")
p.join("a.py").write(textwrap.dedent("""\
import doesnotexist
x = 1
"""))
testdir.tmpdir.join("test_importerror.py").write(textwrap.dedent("""\
def test_importerror(monkeypatch):
monkeypatch.setattr('package.a.x', 2)
"""))
result = testdir.runpytest()
result.stdout.fnmatch_lines("""
*import error in package.a.x: No module named {0}doesnotexist{0}*
""".format("'" if sys.version_info > (3, 0) else ""))
class SampleNew(object):