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:
parent
522224ee7c
commit
8d49abb0d1
|
@ -1,8 +1,14 @@
|
||||||
""" monkeypatching and mocking functionality. """
|
""" monkeypatching and mocking functionality. """
|
||||||
|
|
||||||
import os, sys
|
import os, sys
|
||||||
|
import re
|
||||||
|
|
||||||
from py.builtin import _basestring
|
from py.builtin import _basestring
|
||||||
|
|
||||||
|
|
||||||
|
RE_IMPORT_ERROR_NAME = re.compile("^No module named (.*)$")
|
||||||
|
|
||||||
|
|
||||||
def pytest_funcarg__monkeypatch(request):
|
def pytest_funcarg__monkeypatch(request):
|
||||||
"""The returned ``monkeypatch`` funcarg provides these
|
"""The returned ``monkeypatch`` funcarg provides these
|
||||||
helper methods to modify objects, dictionaries or os.environ::
|
helper methods to modify objects, dictionaries or os.environ::
|
||||||
|
@ -34,14 +40,28 @@ def derive_importpath(import_path, raising):
|
||||||
(import_path,))
|
(import_path,))
|
||||||
rest = []
|
rest = []
|
||||||
target = import_path
|
target = import_path
|
||||||
|
target_parts = set(target.split("."))
|
||||||
while target:
|
while target:
|
||||||
try:
|
try:
|
||||||
obj = __import__(target, None, None, "__doc__")
|
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:
|
if "." not in target:
|
||||||
__tracebackhide__ = True
|
__tracebackhide__ = True
|
||||||
pytest.fail("could not import any sub part: %s" %
|
pytest.fail("could not import any sub part: %s" %
|
||||||
import_path)
|
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)
|
target, name = target.rsplit(".", 1)
|
||||||
rest.append(name)
|
rest.append(name)
|
||||||
else:
|
else:
|
||||||
|
|
|
@ -1,4 +1,6 @@
|
||||||
import os, sys
|
import os, sys
|
||||||
|
import textwrap
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
from _pytest.monkeypatch import monkeypatch as MonkeyPatch
|
from _pytest.monkeypatch import monkeypatch as MonkeyPatch
|
||||||
|
|
||||||
|
@ -245,6 +247,21 @@ def test_issue185_time_breaks(testdir):
|
||||||
*1 passed*
|
*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):
|
class SampleNew(object):
|
||||||
|
|
Loading…
Reference in New Issue