Skip to content

monkeypatch.syspath_prepend: call fixup_namespace_packages #4980

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Mar 25, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions changelog/4980.feature.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Namespace packages are handled better with ``monkeypatch.syspath_prepend`` and ``testdir.syspathinsert`` (via ``pkg_resources.fixup_namespace_packages``).
5 changes: 5 additions & 0 deletions src/_pytest/monkeypatch.py
Original file line number Diff line number Diff line change
Expand Up @@ -262,10 +262,15 @@ def delenv(self, name, raising=True):

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.
Expand Down
17 changes: 10 additions & 7 deletions src/_pytest/pytester.py
Original file line number Diff line number Diff line change
Expand Up @@ -593,11 +593,16 @@ def syspathinsert(self, path=None):

This is undone automatically when this object dies at the end of each
test.

"""
from pkg_resources import fixup_namespace_packages

if path is None:
path = self.tmpdir
sys.path.insert(0, str(path))

dirname = str(path)
sys.path.insert(0, dirname)
fixup_namespace_packages(dirname)

# a call to syspathinsert() usually means that the caller wants to
# import some dynamically created files, thus with python3 we
# invalidate its import caches
Expand All @@ -606,12 +611,10 @@ def syspathinsert(self, path=None):
def _possibly_invalidate_import_caches(self):
# invalidate caches if we can (py33 and above)
try:
import importlib
from importlib import invalidate_caches
except ImportError:
pass
else:
if hasattr(importlib, "invalidate_caches"):
importlib.invalidate_caches()
return
invalidate_caches()

def mkdir(self, name):
"""Create a new (sub)directory."""
Expand Down
2 changes: 0 additions & 2 deletions testing/python/collect.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,6 @@ def test_import_duplicate(self, testdir):
)

def test_import_prepend_append(self, testdir, monkeypatch):
syspath = list(sys.path)
monkeypatch.setattr(sys, "path", syspath)
root1 = testdir.mkdir("root1")
root2 = testdir.mkdir("root2")
root1.ensure("x456.py")
Expand Down
2 changes: 1 addition & 1 deletion testing/test_assertrewrite.py
Original file line number Diff line number Diff line change
Expand Up @@ -1335,7 +1335,7 @@ def test_cwd_changed(self, testdir, monkeypatch):
# Setup conditions for py's fspath trying to import pathlib on py34
# always (previously triggered via xdist only).
# Ref: https://github.com/pytest-dev/py/pull/207
monkeypatch.setattr(sys, "path", [""] + sys.path)
monkeypatch.syspath_prepend("")
monkeypatch.delitem(sys.modules, "pathlib", raising=False)

testdir.makepyfile(
Expand Down
25 changes: 25 additions & 0 deletions testing/test_monkeypatch.py
Original file line number Diff line number Diff line change
Expand Up @@ -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"
9 changes: 4 additions & 5 deletions testing/test_pluginmanager.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
from __future__ import print_function

import os
import re
import sys
import types

Expand Down Expand Up @@ -165,10 +164,10 @@ def test_traceback():
with pytest.raises(ImportError) as excinfo:
pytestpm.import_plugin("qwe")

expected_message = '.*Error importing plugin "qwe": Not possible to import: .'
expected_traceback = ".*in test_traceback"
assert re.match(expected_message, str(excinfo.value))
assert re.match(expected_traceback, str(excinfo.traceback[-1]))
assert str(excinfo.value).endswith(
'Error importing plugin "qwe": Not possible to import: ☺'
)
assert "in test_traceback" in str(excinfo.traceback[-1])


class TestPytestPluginManager(object):
Expand Down