# XXX import lazy_mixin import lazy_proxy import importlib import importlib.abc import sys from test import support import unittest class LazyLoaderTests: module_name = 'lazy_loader_test' def setUp(self): self.load_count = 0 class MockLoader(importlib.abc.Loader): @classmethod def load_module(cls, fullname): self.load_count += 1 module = sys.modules[fullname] module.__loader__ = cls module.loaded = True return module self._mock_loader = MockLoader def tearDown(self): support.forget(self.module_name) def test_load_is_lazy(self): # Using the lazy loader does not cause the module to be loaded but insert # a lazy module into sys.modules. module = self.load_module() self.assertIn(self.module_name, sys.modules) self.assertEqual(self.load_count, 0) def test_load_on_attr_read(self): # Reading an attribute from the module triggers the load. # Subsequent attribute access has no ill effects. module = self.load_module() self.assertEqual(self.load_count, 0) self.assertEqual(module.__name__, self.module_name) # Triggers load. self.assertEqual(self.load_count, 1) self.assertIsNone(module.__doc__) # Should not reload on every attribute access. self.assertEqual(self.load_count, 1) def test_assignment_propagates(self): # If an attribute is assigned to, it survives the loading. # This include attribute which are inherent to a module. module = self.load_module() module.__name__ = 'fake name' module.new_attr = -13 self.assertEqual(module.__name__, 'fake name') # Triggers load. self.assertEqual(module.new_attr, -13) def test_loading_failure(self): # If a module fails to load then make sure it is cleared from sys.modules. class FailingLoader(importlib.abc.Loader): @staticmethod def load_module(fullname): raise ImportError('forced failure') loader = self.load_module(FailingLoader) assert self.module_name in sys.modules with self.assertRaises(ImportError): loader.attr # Avoiding assertNotIn() so as to not print out sys.modules. self.assertFalse(self.module_name in sys.modules, '{} unexpectedly found in sys.modules'.format(self.module_name)) with self.assertRaises(ImportError): loader.attr def test_reload_immediately(self): # Reloading a module works. module = self.load_module() importlib.reload(module) self.assertEqual(module.__name__, self.module_name) def test_reload_later(self): assert self.load_count == 0 module = self.load_module() self.assertEqual(module.__name__, self.module_name) # Triggers load. self.assertEqual(self.load_count, 1) self.assertTrue(module.loaded) self.assertIsNotNone(module.__loader__) module.loaded = False importlib.reload(module) self.assertTrue(module.loaded) self.assertEqual(self.load_count, 2) class LazyMixinTests(LazyLoaderTests, unittest.TestCase): def load_module(self, real_loader=None): if real_loader is None: real_loader = self._mock_loader class LazyLoader(lazy_mixin.Mixin, real_loader): pass return LazyLoader().load_module(self.module_name) class LazyProxyTests(LazyLoaderTests, unittest.TestCase): def load_module(self, real_loader=None): if real_loader is None: real_loader = self._mock_loader loader = lazy_proxy.LazyProxy(real_loader)() return loader.load_module(self.module_name) if __name__ == '__main__': unittest.main()