import contextlib import importlib import importlib.abc import importlib.machinery import os import sys import tempfile import unittest from test.test_importlib import util # needed tests: # # need to test when nested, so that the top-level path isn't sys.path # need to test dynamic path detection, both at top-level and nested # with dynamic path, check when a loader is returned on path reload (that is, # trying to switch from a namespace package to a regular package) @contextlib.contextmanager def sys_modules_context(): """ Make sure sys.modules is the same object and has the same content when exiting the context as when entering. Similar to importlib.test.util.uncache, but doesn't require explicit names. """ sys_modules_saved = sys.modules sys_modules_copy = sys.modules.copy() try: yield finally: sys.modules = sys_modules_saved sys.modules.clear() sys.modules.update(sys_modules_copy) @contextlib.contextmanager def namespace_tree_context(**kwargs): """ Save import state and sys.modules cache and restore it on exit. Typical usage: >>> with namespace_tree_context(path=['/tmp/xxyy/portion1', ... '/tmp/xxyy/portion2']): ... pass """ # use default meta_path and path_hooks unless specified otherwise kwargs.setdefault('meta_path', sys.meta_path) kwargs.setdefault('path_hooks', sys.path_hooks) import_context = util.import_state(**kwargs) with import_context, sys_modules_context(): yield class NamespacePackageTest(unittest.TestCase): """ Subclasses should define self.root and self.paths (under that root) to be added to sys.path. """ root = os.path.join(os.path.dirname(__file__), 'namespace_pkgs') def setUp(self): self.resolved_paths = [ os.path.join(self.root, path) for path in self.paths ] self.enterContext(namespace_tree_context(path=self.resolved_paths)) class SingleNamespacePackage(NamespacePackageTest): paths = ['portion1'] def test_simple_package(self): import foo.one self.assertEqual(foo.one.attr, 'portion1 foo one') def test_cant_import_other(self): with self.assertRaises(ImportError): import foo.two def test_simple_repr(self): import foo.one assert repr(foo).startswith("