diff --git a/changelog/4980.feature.rst b/changelog/4980.feature.rst new file mode 100644 index 000000000..1c42547c1 --- /dev/null +++ b/changelog/4980.feature.rst @@ -0,0 +1 @@ +``monkeypatch.syspath_prepend`` calls ``pkg_resources.fixup_namespace_packages`` to handle namespace packages better. diff --git a/src/_pytest/monkeypatch.py b/src/_pytest/monkeypatch.py index 46d9718da..f6c134664 100644 --- a/src/_pytest/monkeypatch.py +++ b/src/_pytest/monkeypatch.py @@ -262,10 +262,15 @@ class MonkeyPatch(object): def syspath_prepend(self, path): """ Prepend ``path`` to ``sys.path`` list of import locations. """ + from pkg_resources import fixup_namespace_packages + if self._savesyspath is None: self._savesyspath = sys.path[:] sys.path.insert(0, str(path)) + # https://github.com/pypa/setuptools/blob/d8b901bc/docs/pkg_resources.txt#L162-L171 + fixup_namespace_packages(str(path)) + def chdir(self, path): """ Change the current working directory to the specified path. Path can be a string or a py.path.local object. diff --git a/testing/test_monkeypatch.py b/testing/test_monkeypatch.py index 0a953d3f1..d43fb6bab 100644 --- a/testing/test_monkeypatch.py +++ b/testing/test_monkeypatch.py @@ -437,3 +437,28 @@ def test_context(): m.setattr(functools, "partial", 3) assert not inspect.isclass(functools.partial) assert inspect.isclass(functools.partial) + + +def test_syspath_prepend_with_namespace_packages(testdir, monkeypatch): + for dirname in "hello", "world": + d = testdir.mkdir(dirname) + ns = d.mkdir("ns_pkg") + ns.join("__init__.py").write( + "__import__('pkg_resources').declare_namespace(__name__)" + ) + lib = ns.mkdir(dirname) + lib.join("__init__.py").write("def check(): return %r" % dirname) + + monkeypatch.syspath_prepend("hello") + import ns_pkg.hello + + assert ns_pkg.hello.check() == "hello" + + with pytest.raises(ImportError): + import ns_pkg.world + + # Prepending should call fixup_namespace_packages. + monkeypatch.syspath_prepend("world") + import ns_pkg.world + + assert ns_pkg.world.check() == "world"