summaryrefslogtreecommitdiff
path: root/Lib/test/test_importlib/test_abc.py
diff options
context:
space:
mode:
authorBrett Cannon <brett@python.org>2013-05-31 18:56:47 -0400
committerBrett Cannon <brett@python.org>2013-05-31 18:56:47 -0400
commitb0f5285b4783f020fddf50d9e113f5b9429fa382 (patch)
treea89206a0b3537954fc7224e100eb50d2eacd2cab /Lib/test/test_importlib/test_abc.py
parentd56eea2a5c0f5fc22f5802c4e9dad597968fe247 (diff)
downloadcpython-b0f5285b4783f020fddf50d9e113f5b9429fa382.tar.gz
Issues #18088, 18089: Introduce
importlib.abc.Loader.init_module_attrs() and implement importlib.abc.InspectLoader.load_module(). The importlib.abc.Loader.init_module_attrs() method sets the various attributes on the module being loaded. It is done unconditionally to support reloading. Typically people used importlib.util.module_for_loader, but since that's a decorator there was no way to override it's actions, so init_module_attrs() came into existence to allow for overriding. This is also why module_for_loader is now pending deprecation (having its other use replaced by importlib.util.module_to_load). All of this allowed for importlib.abc.InspectLoader.load_module() to be implemented. At this point you can now implement a loader with nothing more than get_code() (which only requires get_source(); package support requires is_package()). Thanks to init_module_attrs() the implementation of load_module() is basically a context manager containing 2 methods calls, a call to exec(), and a return statement.
Diffstat (limited to 'Lib/test/test_importlib/test_abc.py')
-rw-r--r--Lib/test/test_importlib/test_abc.py202
1 files changed, 202 insertions, 0 deletions
diff --git a/Lib/test/test_importlib/test_abc.py b/Lib/test/test_importlib/test_abc.py
index b443337ed5..ea417f51e3 100644
--- a/Lib/test/test_importlib/test_abc.py
+++ b/Lib/test/test_importlib/test_abc.py
@@ -2,12 +2,14 @@ import importlib
from importlib import abc
from importlib import machinery
+import contextlib
import imp
import inspect
import io
import marshal
import os
import sys
+from test import support
import unittest
from unittest import mock
@@ -198,6 +200,15 @@ class ExecutionLoaderDefaultsTests(unittest.TestCase):
with self.assertRaises(ImportError):
self.ins.get_filename('blah')
+##### Loader concrete methods ##################################################
+class LoaderConcreteMethodTests(unittest.TestCase):
+
+ def test_init_module_attrs(self):
+ loader = LoaderSubclass()
+ module = imp.new_module('blah')
+ loader.init_module_attrs(module)
+ self.assertEqual(module.__loader__, loader)
+
##### InspectLoader concrete methods ###########################################
class InspectLoaderSourceToCodeTests(unittest.TestCase):
@@ -269,6 +280,93 @@ class InspectLoaderGetCodeTests(unittest.TestCase):
loader.get_code('blah')
+class InspectLoaderInitModuleTests(unittest.TestCase):
+
+ @staticmethod
+ def mock_is_package(return_value):
+ return mock.patch.object(InspectLoaderSubclass, 'is_package',
+ return_value=return_value)
+
+ def init_module_attrs(self, name):
+ loader = InspectLoaderSubclass()
+ module = imp.new_module(name)
+ loader.init_module_attrs(module)
+ self.assertEqual(module.__loader__, loader)
+ return module
+
+ def test_package(self):
+ # If a package, then __package__ == __name__, __path__ == []
+ with self.mock_is_package(True):
+ name = 'blah'
+ module = self.init_module_attrs(name)
+ self.assertEqual(module.__package__, name)
+ self.assertEqual(module.__path__, [])
+
+ def test_toplevel(self):
+ # If a module is top-level, __package__ == ''
+ with self.mock_is_package(False):
+ name = 'blah'
+ module = self.init_module_attrs(name)
+ self.assertEqual(module.__package__, '')
+
+ def test_submodule(self):
+ # If a module is contained within a package then set __package__ to the
+ # package name.
+ with self.mock_is_package(False):
+ name = 'pkg.mod'
+ module = self.init_module_attrs(name)
+ self.assertEqual(module.__package__, 'pkg')
+
+ def test_is_package_ImportError(self):
+ # If is_package() raises ImportError, __package__ should be None and
+ # __path__ should not be set.
+ with self.mock_is_package(False) as mocked_method:
+ mocked_method.side_effect = ImportError
+ name = 'mod'
+ module = self.init_module_attrs(name)
+ self.assertIsNone(module.__package__)
+ self.assertFalse(hasattr(module, '__path__'))
+
+
+class InspectLoaderLoadModuleTests(unittest.TestCase):
+
+ """Test InspectLoader.load_module()."""
+
+ module_name = 'blah'
+
+ def setUp(self):
+ support.unload(self.module_name)
+ self.addCleanup(support.unload, self.module_name)
+
+ def mock_get_code(self):
+ return mock.patch.object(InspectLoaderSubclass, 'get_code')
+
+ def test_get_code_ImportError(self):
+ # If get_code() raises ImportError, it should propagate.
+ with self.mock_get_code() as mocked_get_code:
+ mocked_get_code.side_effect = ImportError
+ with self.assertRaises(ImportError):
+ loader = InspectLoaderSubclass()
+ loader.load_module(self.module_name)
+
+ def test_get_code_None(self):
+ # If get_code() returns None, raise ImportError.
+ with self.mock_get_code() as mocked_get_code:
+ mocked_get_code.return_value = None
+ with self.assertRaises(ImportError):
+ loader = InspectLoaderSubclass()
+ loader.load_module(self.module_name)
+
+ def test_module_returned(self):
+ # The loaded module should be returned.
+ code = compile('attr = 42', '<string>', 'exec')
+ with self.mock_get_code() as mocked_get_code:
+ mocked_get_code.return_value = code
+ loader = InspectLoaderSubclass()
+ module = loader.load_module(self.module_name)
+ self.assertEqual(module, sys.modules[self.module_name])
+
+
##### ExecutionLoader concrete methods #########################################
class ExecutionLoaderGetCodeTests(unittest.TestCase):
@@ -327,6 +425,69 @@ class ExecutionLoaderGetCodeTests(unittest.TestCase):
self.assertEqual(module.attr, 42)
+class ExecutionLoaderInitModuleTests(unittest.TestCase):
+
+ @staticmethod
+ @contextlib.contextmanager
+ def mock_methods(is_package, filename):
+ is_package_manager = InspectLoaderInitModuleTests.mock_is_package(is_package)
+ get_filename_manager = mock.patch.object(ExecutionLoaderSubclass,
+ 'get_filename', return_value=filename)
+ with is_package_manager as mock_is_package:
+ with get_filename_manager as mock_get_filename:
+ yield {'is_package': mock_is_package,
+ 'get_filename': mock_get_filename}
+
+ def test_toplevel(self):
+ # Verify __loader__, __file__, and __package__; no __path__.
+ name = 'blah'
+ path = os.path.join('some', 'path', '{}.py'.format(name))
+ with self.mock_methods(False, path):
+ loader = ExecutionLoaderSubclass()
+ module = imp.new_module(name)
+ loader.init_module_attrs(module)
+ self.assertIs(module.__loader__, loader)
+ self.assertEqual(module.__file__, path)
+ self.assertEqual(module.__package__, '')
+ self.assertFalse(hasattr(module, '__path__'))
+
+ def test_package(self):
+ # Verify __loader__, __file__, __package__, and __path__.
+ name = 'pkg'
+ path = os.path.join('some', 'pkg', '__init__.py')
+ with self.mock_methods(True, path):
+ loader = ExecutionLoaderSubclass()
+ module = imp.new_module(name)
+ loader.init_module_attrs(module)
+ self.assertIs(module.__loader__, loader)
+ self.assertEqual(module.__file__, path)
+ self.assertEqual(module.__package__, 'pkg')
+ self.assertEqual(module.__path__, [os.path.dirname(path)])
+
+ def test_submodule(self):
+ # Verify __package__ and not __path__; test_toplevel() takes care of
+ # other attributes.
+ name = 'pkg.submodule'
+ path = os.path.join('some', 'pkg', 'submodule.py')
+ with self.mock_methods(False, path):
+ loader = ExecutionLoaderSubclass()
+ module = imp.new_module(name)
+ loader.init_module_attrs(module)
+ self.assertEqual(module.__package__, 'pkg')
+ self.assertEqual(module.__file__, path)
+ self.assertFalse(hasattr(module, '__path__'))
+
+ def test_get_filename_ImportError(self):
+ # If get_filename() raises ImportError, don't set __file__.
+ name = 'blah'
+ path = 'blah.py'
+ with self.mock_methods(False, path) as mocked_methods:
+ mocked_methods['get_filename'].side_effect = ImportError
+ loader = ExecutionLoaderSubclass()
+ module = imp.new_module(name)
+ loader.init_module_attrs(module)
+ self.assertFalse(hasattr(module, '__file__'))
+
##### SourceLoader concrete methods ############################################
class SourceOnlyLoaderMock(abc.SourceLoader):
@@ -621,6 +782,47 @@ class SourceLoaderGetSourceTests(unittest.TestCase):
self.assertEqual(mock.get_source(name), expect)
+class SourceLoaderInitModuleAttrTests(unittest.TestCase):
+
+ """Tests for importlib.abc.SourceLoader.init_module_attrs()."""
+
+ def test_init_module_attrs(self):
+ # If __file__ set, __cached__ == imp.cached_from_source(__file__).
+ name = 'blah'
+ path = 'blah.py'
+ loader = SourceOnlyLoaderMock(path)
+ module = imp.new_module(name)
+ loader.init_module_attrs(module)
+ self.assertEqual(module.__loader__, loader)
+ self.assertEqual(module.__package__, '')
+ self.assertEqual(module.__file__, path)
+ self.assertEqual(module.__cached__, imp.cache_from_source(path))
+
+ @mock.patch('importlib._bootstrap.cache_from_source')
+ def test_cache_from_source_NotImplementedError(self, mock_cache_from_source):
+ # If imp.cache_from_source() raises NotImplementedError don't set
+ # __cached__.
+ mock_cache_from_source.side_effect = NotImplementedError
+ name = 'blah'
+ path = 'blah.py'
+ loader = SourceOnlyLoaderMock(path)
+ module = imp.new_module(name)
+ loader.init_module_attrs(module)
+ self.assertEqual(module.__file__, path)
+ self.assertFalse(hasattr(module, '__cached__'))
+
+ def test_no_get_filename(self):
+ # No __file__, no __cached__.
+ with mock.patch.object(SourceOnlyLoaderMock, 'get_filename') as mocked:
+ mocked.side_effect = ImportError
+ name = 'blah'
+ loader = SourceOnlyLoaderMock('blah.py')
+ module = imp.new_module(name)
+ loader.init_module_attrs(module)
+ self.assertFalse(hasattr(module, '__file__'))
+ self.assertFalse(hasattr(module, '__cached__'))
+
+
if __name__ == '__main__':
unittest.main()