Brett Cannon | 100883f | 2013-04-09 16:59:39 -0400 | [diff] [blame] | 1 | import importlib |
Brett Cannon | ef88802 | 2013-06-15 18:39:21 -0400 | [diff] [blame] | 2 | import importlib.util |
Brett Cannon | 2a922ed | 2009-03-09 03:35:50 +0000 | [diff] [blame] | 3 | from importlib import abc |
| 4 | from importlib import machinery |
Brett Cannon | 100883f | 2013-04-09 16:59:39 -0400 | [diff] [blame] | 5 | |
Brett Cannon | 0dbb4c7 | 2013-05-31 18:56:47 -0400 | [diff] [blame] | 6 | import contextlib |
Brett Cannon | 64ef00f | 2009-07-20 03:19:18 +0000 | [diff] [blame] | 7 | import inspect |
Brett Cannon | 100883f | 2013-04-09 16:59:39 -0400 | [diff] [blame] | 8 | import io |
| 9 | import marshal |
| 10 | import os |
| 11 | import sys |
Brett Cannon | 0dbb4c7 | 2013-05-31 18:56:47 -0400 | [diff] [blame] | 12 | from test import support |
Brett Cannon | ef88802 | 2013-06-15 18:39:21 -0400 | [diff] [blame] | 13 | import types |
Brett Cannon | 2a922ed | 2009-03-09 03:35:50 +0000 | [diff] [blame] | 14 | import unittest |
Brett Cannon | 3b62ca8 | 2013-05-27 21:11:04 -0400 | [diff] [blame] | 15 | from unittest import mock |
Brett Cannon | 2a922ed | 2009-03-09 03:35:50 +0000 | [diff] [blame] | 16 | |
Brett Cannon | 100883f | 2013-04-09 16:59:39 -0400 | [diff] [blame] | 17 | from . import util |
Brett Cannon | 2a922ed | 2009-03-09 03:35:50 +0000 | [diff] [blame] | 18 | |
Brett Cannon | 9ffe85e | 2013-05-26 16:45:10 -0400 | [diff] [blame] | 19 | ##### Inheritance ############################################################## |
Brett Cannon | 64ef00f | 2009-07-20 03:19:18 +0000 | [diff] [blame] | 20 | class InheritanceTests: |
Brett Cannon | 2a922ed | 2009-03-09 03:35:50 +0000 | [diff] [blame] | 21 | |
Brett Cannon | 64ef00f | 2009-07-20 03:19:18 +0000 | [diff] [blame] | 22 | """Test that the specified class is a subclass/superclass of the expected |
| 23 | classes.""" |
Brett Cannon | 2a922ed | 2009-03-09 03:35:50 +0000 | [diff] [blame] | 24 | |
Brett Cannon | 64ef00f | 2009-07-20 03:19:18 +0000 | [diff] [blame] | 25 | subclasses = [] |
| 26 | superclasses = [] |
Brett Cannon | 2a922ed | 2009-03-09 03:35:50 +0000 | [diff] [blame] | 27 | |
Brett Cannon | 64ef00f | 2009-07-20 03:19:18 +0000 | [diff] [blame] | 28 | def __init__(self, *args, **kwargs): |
| 29 | super().__init__(*args, **kwargs) |
| 30 | assert self.subclasses or self.superclasses, self.__class__ |
| 31 | self.__test = getattr(abc, self.__class__.__name__) |
Brett Cannon | 2a922ed | 2009-03-09 03:35:50 +0000 | [diff] [blame] | 32 | |
Brett Cannon | 64ef00f | 2009-07-20 03:19:18 +0000 | [diff] [blame] | 33 | def test_subclasses(self): |
| 34 | # Test that the expected subclasses inherit. |
| 35 | for subclass in self.subclasses: |
| 36 | self.assertTrue(issubclass(subclass, self.__test), |
| 37 | "{0} is not a subclass of {1}".format(subclass, self.__test)) |
| 38 | |
| 39 | def test_superclasses(self): |
| 40 | # Test that the class inherits from the expected superclasses. |
| 41 | for superclass in self.superclasses: |
| 42 | self.assertTrue(issubclass(self.__test, superclass), |
| 43 | "{0} is not a superclass of {1}".format(superclass, self.__test)) |
| 44 | |
| 45 | |
Nick Coghlan | 8a9080f | 2012-08-02 21:26:03 +1000 | [diff] [blame] | 46 | class MetaPathFinder(InheritanceTests, unittest.TestCase): |
Brett Cannon | 64ef00f | 2009-07-20 03:19:18 +0000 | [diff] [blame] | 47 | |
Nick Coghlan | 8a9080f | 2012-08-02 21:26:03 +1000 | [diff] [blame] | 48 | superclasses = [abc.Finder] |
Brett Cannon | 64ef00f | 2009-07-20 03:19:18 +0000 | [diff] [blame] | 49 | subclasses = [machinery.BuiltinImporter, machinery.FrozenImporter, |
Nick Coghlan | ff79486 | 2012-08-02 21:45:24 +1000 | [diff] [blame] | 50 | machinery.PathFinder, machinery.WindowsRegistryFinder] |
Brett Cannon | 64ef00f | 2009-07-20 03:19:18 +0000 | [diff] [blame] | 51 | |
Brett Cannon | f4dc920 | 2012-08-10 12:21:12 -0400 | [diff] [blame] | 52 | |
Nick Coghlan | 8a9080f | 2012-08-02 21:26:03 +1000 | [diff] [blame] | 53 | class PathEntryFinder(InheritanceTests, unittest.TestCase): |
| 54 | |
| 55 | superclasses = [abc.Finder] |
| 56 | subclasses = [machinery.FileFinder] |
Brett Cannon | 64ef00f | 2009-07-20 03:19:18 +0000 | [diff] [blame] | 57 | |
Brett Cannon | f4dc920 | 2012-08-10 12:21:12 -0400 | [diff] [blame] | 58 | |
Brett Cannon | 64ef00f | 2009-07-20 03:19:18 +0000 | [diff] [blame] | 59 | class ResourceLoader(InheritanceTests, unittest.TestCase): |
| 60 | |
| 61 | superclasses = [abc.Loader] |
| 62 | |
| 63 | |
| 64 | class InspectLoader(InheritanceTests, unittest.TestCase): |
| 65 | |
| 66 | superclasses = [abc.Loader] |
Andrew Svetlov | 90a654b | 2012-11-05 09:34:46 +0200 | [diff] [blame] | 67 | subclasses = [machinery.BuiltinImporter, |
Brett Cannon | 938d44d | 2012-04-22 19:58:33 -0400 | [diff] [blame] | 68 | machinery.FrozenImporter, machinery.ExtensionFileLoader] |
Brett Cannon | 64ef00f | 2009-07-20 03:19:18 +0000 | [diff] [blame] | 69 | |
| 70 | |
Brett Cannon | 6919427 | 2009-07-20 04:23:48 +0000 | [diff] [blame] | 71 | class ExecutionLoader(InheritanceTests, unittest.TestCase): |
| 72 | |
| 73 | superclasses = [abc.InspectLoader] |
Brett Cannon | 6919427 | 2009-07-20 04:23:48 +0000 | [diff] [blame] | 74 | |
| 75 | |
Brett Cannon | 938d44d | 2012-04-22 19:58:33 -0400 | [diff] [blame] | 76 | class FileLoader(InheritanceTests, unittest.TestCase): |
| 77 | |
| 78 | superclasses = [abc.ResourceLoader, abc.ExecutionLoader] |
Marc-Andre Lemburg | 4fe29c9 | 2012-04-25 02:31:37 +0200 | [diff] [blame] | 79 | subclasses = [machinery.SourceFileLoader, machinery.SourcelessFileLoader] |
Brett Cannon | 938d44d | 2012-04-22 19:58:33 -0400 | [diff] [blame] | 80 | |
| 81 | |
Brett Cannon | b7183d8 | 2010-06-28 05:46:25 +0000 | [diff] [blame] | 82 | class SourceLoader(InheritanceTests, unittest.TestCase): |
| 83 | |
| 84 | superclasses = [abc.ResourceLoader, abc.ExecutionLoader] |
Brett Cannon | 938d44d | 2012-04-22 19:58:33 -0400 | [diff] [blame] | 85 | subclasses = [machinery.SourceFileLoader] |
Brett Cannon | b7183d8 | 2010-06-28 05:46:25 +0000 | [diff] [blame] | 86 | |
| 87 | |
Brett Cannon | 9ffe85e | 2013-05-26 16:45:10 -0400 | [diff] [blame] | 88 | ##### Default return values #################################################### |
Brett Cannon | 100883f | 2013-04-09 16:59:39 -0400 | [diff] [blame] | 89 | class MetaPathFinderSubclass(abc.MetaPathFinder): |
| 90 | |
| 91 | def find_module(self, fullname, path): |
| 92 | return super().find_module(fullname, path) |
| 93 | |
| 94 | |
| 95 | class MetaPathFinderDefaultsTests(unittest.TestCase): |
| 96 | |
| 97 | ins = MetaPathFinderSubclass() |
| 98 | |
| 99 | def test_find_module(self): |
| 100 | # Default should return None. |
| 101 | self.assertIsNone(self.ins.find_module('something', None)) |
| 102 | |
| 103 | def test_invalidate_caches(self): |
| 104 | # Calling the method is a no-op. |
| 105 | self.ins.invalidate_caches() |
| 106 | |
| 107 | |
| 108 | class PathEntryFinderSubclass(abc.PathEntryFinder): |
| 109 | |
| 110 | def find_loader(self, fullname): |
| 111 | return super().find_loader(fullname) |
| 112 | |
| 113 | |
| 114 | class PathEntryFinderDefaultsTests(unittest.TestCase): |
| 115 | |
| 116 | ins = PathEntryFinderSubclass() |
| 117 | |
| 118 | def test_find_loader(self): |
| 119 | self.assertEqual((None, []), self.ins.find_loader('something')) |
| 120 | |
| 121 | def find_module(self): |
| 122 | self.assertEqual(None, self.ins.find_module('something')) |
| 123 | |
| 124 | def test_invalidate_caches(self): |
| 125 | # Should be a no-op. |
| 126 | self.ins.invalidate_caches() |
| 127 | |
| 128 | |
| 129 | class LoaderSubclass(abc.Loader): |
| 130 | |
| 131 | def load_module(self, fullname): |
| 132 | return super().load_module(fullname) |
| 133 | |
| 134 | |
| 135 | class LoaderDefaultsTests(unittest.TestCase): |
| 136 | |
| 137 | ins = LoaderSubclass() |
| 138 | |
| 139 | def test_load_module(self): |
| 140 | with self.assertRaises(ImportError): |
| 141 | self.ins.load_module('something') |
| 142 | |
| 143 | def test_module_repr(self): |
Brett Cannon | ef88802 | 2013-06-15 18:39:21 -0400 | [diff] [blame] | 144 | mod = types.ModuleType('blah') |
Brett Cannon | 100883f | 2013-04-09 16:59:39 -0400 | [diff] [blame] | 145 | with self.assertRaises(NotImplementedError): |
| 146 | self.ins.module_repr(mod) |
| 147 | original_repr = repr(mod) |
| 148 | mod.__loader__ = self.ins |
| 149 | # Should still return a proper repr. |
| 150 | self.assertTrue(repr(mod)) |
| 151 | |
| 152 | |
| 153 | class ResourceLoaderSubclass(LoaderSubclass, abc.ResourceLoader): |
| 154 | |
| 155 | def get_data(self, path): |
| 156 | return super().get_data(path) |
| 157 | |
| 158 | |
| 159 | class ResourceLoaderDefaultsTests(unittest.TestCase): |
| 160 | |
| 161 | ins = ResourceLoaderSubclass() |
| 162 | |
| 163 | def test_get_data(self): |
| 164 | with self.assertRaises(IOError): |
| 165 | self.ins.get_data('/some/path') |
| 166 | |
| 167 | |
| 168 | class InspectLoaderSubclass(LoaderSubclass, abc.InspectLoader): |
| 169 | |
| 170 | def is_package(self, fullname): |
| 171 | return super().is_package(fullname) |
| 172 | |
Brett Cannon | 100883f | 2013-04-09 16:59:39 -0400 | [diff] [blame] | 173 | def get_source(self, fullname): |
| 174 | return super().get_source(fullname) |
| 175 | |
| 176 | |
| 177 | class InspectLoaderDefaultsTests(unittest.TestCase): |
| 178 | |
| 179 | ins = InspectLoaderSubclass() |
| 180 | |
| 181 | def test_is_package(self): |
| 182 | with self.assertRaises(ImportError): |
| 183 | self.ins.is_package('blah') |
| 184 | |
Brett Cannon | 100883f | 2013-04-09 16:59:39 -0400 | [diff] [blame] | 185 | def test_get_source(self): |
| 186 | with self.assertRaises(ImportError): |
| 187 | self.ins.get_source('blah') |
| 188 | |
| 189 | |
| 190 | class ExecutionLoaderSubclass(InspectLoaderSubclass, abc.ExecutionLoader): |
| 191 | |
| 192 | def get_filename(self, fullname): |
| 193 | return super().get_filename(fullname) |
| 194 | |
| 195 | |
| 196 | class ExecutionLoaderDefaultsTests(unittest.TestCase): |
| 197 | |
| 198 | ins = ExecutionLoaderSubclass() |
| 199 | |
| 200 | def test_get_filename(self): |
| 201 | with self.assertRaises(ImportError): |
| 202 | self.ins.get_filename('blah') |
| 203 | |
Brett Cannon | 0dbb4c7 | 2013-05-31 18:56:47 -0400 | [diff] [blame] | 204 | ##### Loader concrete methods ################################################## |
| 205 | class LoaderConcreteMethodTests(unittest.TestCase): |
| 206 | |
| 207 | def test_init_module_attrs(self): |
| 208 | loader = LoaderSubclass() |
Brett Cannon | ef88802 | 2013-06-15 18:39:21 -0400 | [diff] [blame] | 209 | module = types.ModuleType('blah') |
Brett Cannon | 0dbb4c7 | 2013-05-31 18:56:47 -0400 | [diff] [blame] | 210 | loader.init_module_attrs(module) |
| 211 | self.assertEqual(module.__loader__, loader) |
| 212 | |
Brett Cannon | 100883f | 2013-04-09 16:59:39 -0400 | [diff] [blame] | 213 | |
Brett Cannon | 9ffe85e | 2013-05-26 16:45:10 -0400 | [diff] [blame] | 214 | ##### InspectLoader concrete methods ########################################### |
Brett Cannon | 3b62ca8 | 2013-05-27 21:11:04 -0400 | [diff] [blame] | 215 | class InspectLoaderSourceToCodeTests(unittest.TestCase): |
Brett Cannon | 9ffe85e | 2013-05-26 16:45:10 -0400 | [diff] [blame] | 216 | |
| 217 | def source_to_module(self, data, path=None): |
| 218 | """Help with source_to_code() tests.""" |
Brett Cannon | ef88802 | 2013-06-15 18:39:21 -0400 | [diff] [blame] | 219 | module = types.ModuleType('blah') |
Brett Cannon | 9ffe85e | 2013-05-26 16:45:10 -0400 | [diff] [blame] | 220 | loader = InspectLoaderSubclass() |
| 221 | if path is None: |
| 222 | code = loader.source_to_code(data) |
| 223 | else: |
| 224 | code = loader.source_to_code(data, path) |
| 225 | exec(code, module.__dict__) |
| 226 | return module |
| 227 | |
| 228 | def test_source_to_code_source(self): |
| 229 | # Since compile() can handle strings, so should source_to_code(). |
| 230 | source = 'attr = 42' |
| 231 | module = self.source_to_module(source) |
| 232 | self.assertTrue(hasattr(module, 'attr')) |
| 233 | self.assertEqual(module.attr, 42) |
| 234 | |
| 235 | def test_source_to_code_bytes(self): |
| 236 | # Since compile() can handle bytes, so should source_to_code(). |
| 237 | source = b'attr = 42' |
| 238 | module = self.source_to_module(source) |
| 239 | self.assertTrue(hasattr(module, 'attr')) |
| 240 | self.assertEqual(module.attr, 42) |
| 241 | |
| 242 | def test_source_to_code_path(self): |
| 243 | # Specifying a path should set it for the code object. |
| 244 | path = 'path/to/somewhere' |
| 245 | loader = InspectLoaderSubclass() |
| 246 | code = loader.source_to_code('', path) |
| 247 | self.assertEqual(code.co_filename, path) |
| 248 | |
| 249 | def test_source_to_code_no_path(self): |
| 250 | # Not setting a path should still work and be set to <string> since that |
| 251 | # is a pre-existing practice as a default to compile(). |
| 252 | loader = InspectLoaderSubclass() |
| 253 | code = loader.source_to_code('') |
| 254 | self.assertEqual(code.co_filename, '<string>') |
| 255 | |
| 256 | |
Brett Cannon | 3b62ca8 | 2013-05-27 21:11:04 -0400 | [diff] [blame] | 257 | class InspectLoaderGetCodeTests(unittest.TestCase): |
| 258 | |
| 259 | def test_get_code(self): |
| 260 | # Test success. |
Brett Cannon | ef88802 | 2013-06-15 18:39:21 -0400 | [diff] [blame] | 261 | module = types.ModuleType('blah') |
Brett Cannon | 3b62ca8 | 2013-05-27 21:11:04 -0400 | [diff] [blame] | 262 | with mock.patch.object(InspectLoaderSubclass, 'get_source') as mocked: |
| 263 | mocked.return_value = 'attr = 42' |
| 264 | loader = InspectLoaderSubclass() |
| 265 | code = loader.get_code('blah') |
| 266 | exec(code, module.__dict__) |
| 267 | self.assertEqual(module.attr, 42) |
| 268 | |
| 269 | def test_get_code_source_is_None(self): |
| 270 | # If get_source() is None then this should be None. |
| 271 | with mock.patch.object(InspectLoaderSubclass, 'get_source') as mocked: |
| 272 | mocked.return_value = None |
| 273 | loader = InspectLoaderSubclass() |
| 274 | code = loader.get_code('blah') |
| 275 | self.assertIsNone(code) |
| 276 | |
| 277 | def test_get_code_source_not_found(self): |
| 278 | # If there is no source then there is no code object. |
| 279 | loader = InspectLoaderSubclass() |
| 280 | with self.assertRaises(ImportError): |
| 281 | loader.get_code('blah') |
| 282 | |
| 283 | |
Brett Cannon | 0dbb4c7 | 2013-05-31 18:56:47 -0400 | [diff] [blame] | 284 | class InspectLoaderInitModuleTests(unittest.TestCase): |
| 285 | |
| 286 | @staticmethod |
| 287 | def mock_is_package(return_value): |
| 288 | return mock.patch.object(InspectLoaderSubclass, 'is_package', |
| 289 | return_value=return_value) |
| 290 | |
| 291 | def init_module_attrs(self, name): |
| 292 | loader = InspectLoaderSubclass() |
Brett Cannon | ef88802 | 2013-06-15 18:39:21 -0400 | [diff] [blame] | 293 | module = types.ModuleType(name) |
Brett Cannon | 0dbb4c7 | 2013-05-31 18:56:47 -0400 | [diff] [blame] | 294 | loader.init_module_attrs(module) |
| 295 | self.assertEqual(module.__loader__, loader) |
| 296 | return module |
| 297 | |
| 298 | def test_package(self): |
| 299 | # If a package, then __package__ == __name__, __path__ == [] |
| 300 | with self.mock_is_package(True): |
| 301 | name = 'blah' |
| 302 | module = self.init_module_attrs(name) |
| 303 | self.assertEqual(module.__package__, name) |
| 304 | self.assertEqual(module.__path__, []) |
| 305 | |
| 306 | def test_toplevel(self): |
| 307 | # If a module is top-level, __package__ == '' |
| 308 | with self.mock_is_package(False): |
| 309 | name = 'blah' |
| 310 | module = self.init_module_attrs(name) |
| 311 | self.assertEqual(module.__package__, '') |
| 312 | |
| 313 | def test_submodule(self): |
| 314 | # If a module is contained within a package then set __package__ to the |
| 315 | # package name. |
| 316 | with self.mock_is_package(False): |
| 317 | name = 'pkg.mod' |
| 318 | module = self.init_module_attrs(name) |
| 319 | self.assertEqual(module.__package__, 'pkg') |
| 320 | |
| 321 | def test_is_package_ImportError(self): |
| 322 | # If is_package() raises ImportError, __package__ should be None and |
| 323 | # __path__ should not be set. |
| 324 | with self.mock_is_package(False) as mocked_method: |
| 325 | mocked_method.side_effect = ImportError |
| 326 | name = 'mod' |
| 327 | module = self.init_module_attrs(name) |
| 328 | self.assertIsNone(module.__package__) |
| 329 | self.assertFalse(hasattr(module, '__path__')) |
| 330 | |
| 331 | |
| 332 | class InspectLoaderLoadModuleTests(unittest.TestCase): |
| 333 | |
| 334 | """Test InspectLoader.load_module().""" |
| 335 | |
| 336 | module_name = 'blah' |
| 337 | |
| 338 | def setUp(self): |
| 339 | support.unload(self.module_name) |
| 340 | self.addCleanup(support.unload, self.module_name) |
| 341 | |
| 342 | def mock_get_code(self): |
| 343 | return mock.patch.object(InspectLoaderSubclass, 'get_code') |
| 344 | |
| 345 | def test_get_code_ImportError(self): |
| 346 | # If get_code() raises ImportError, it should propagate. |
| 347 | with self.mock_get_code() as mocked_get_code: |
| 348 | mocked_get_code.side_effect = ImportError |
| 349 | with self.assertRaises(ImportError): |
| 350 | loader = InspectLoaderSubclass() |
| 351 | loader.load_module(self.module_name) |
| 352 | |
| 353 | def test_get_code_None(self): |
| 354 | # If get_code() returns None, raise ImportError. |
| 355 | with self.mock_get_code() as mocked_get_code: |
| 356 | mocked_get_code.return_value = None |
| 357 | with self.assertRaises(ImportError): |
| 358 | loader = InspectLoaderSubclass() |
| 359 | loader.load_module(self.module_name) |
| 360 | |
| 361 | def test_module_returned(self): |
| 362 | # The loaded module should be returned. |
| 363 | code = compile('attr = 42', '<string>', 'exec') |
| 364 | with self.mock_get_code() as mocked_get_code: |
| 365 | mocked_get_code.return_value = code |
| 366 | loader = InspectLoaderSubclass() |
| 367 | module = loader.load_module(self.module_name) |
| 368 | self.assertEqual(module, sys.modules[self.module_name]) |
| 369 | |
| 370 | |
Brett Cannon | 3b62ca8 | 2013-05-27 21:11:04 -0400 | [diff] [blame] | 371 | ##### ExecutionLoader concrete methods ######################################### |
| 372 | class ExecutionLoaderGetCodeTests(unittest.TestCase): |
| 373 | |
| 374 | def mock_methods(self, *, get_source=False, get_filename=False): |
| 375 | source_mock_context, filename_mock_context = None, None |
| 376 | if get_source: |
| 377 | source_mock_context = mock.patch.object(ExecutionLoaderSubclass, |
| 378 | 'get_source') |
| 379 | if get_filename: |
| 380 | filename_mock_context = mock.patch.object(ExecutionLoaderSubclass, |
| 381 | 'get_filename') |
| 382 | return source_mock_context, filename_mock_context |
| 383 | |
| 384 | def test_get_code(self): |
| 385 | path = 'blah.py' |
| 386 | source_mock_context, filename_mock_context = self.mock_methods( |
| 387 | get_source=True, get_filename=True) |
| 388 | with source_mock_context as source_mock, filename_mock_context as name_mock: |
| 389 | source_mock.return_value = 'attr = 42' |
| 390 | name_mock.return_value = path |
| 391 | loader = ExecutionLoaderSubclass() |
| 392 | code = loader.get_code('blah') |
| 393 | self.assertEqual(code.co_filename, path) |
Brett Cannon | ef88802 | 2013-06-15 18:39:21 -0400 | [diff] [blame] | 394 | module = types.ModuleType('blah') |
Brett Cannon | 3b62ca8 | 2013-05-27 21:11:04 -0400 | [diff] [blame] | 395 | exec(code, module.__dict__) |
| 396 | self.assertEqual(module.attr, 42) |
| 397 | |
| 398 | def test_get_code_source_is_None(self): |
| 399 | # If get_source() is None then this should be None. |
| 400 | source_mock_context, _ = self.mock_methods(get_source=True) |
| 401 | with source_mock_context as mocked: |
| 402 | mocked.return_value = None |
| 403 | loader = ExecutionLoaderSubclass() |
| 404 | code = loader.get_code('blah') |
| 405 | self.assertIsNone(code) |
| 406 | |
| 407 | def test_get_code_source_not_found(self): |
| 408 | # If there is no source then there is no code object. |
| 409 | loader = ExecutionLoaderSubclass() |
| 410 | with self.assertRaises(ImportError): |
| 411 | loader.get_code('blah') |
| 412 | |
| 413 | def test_get_code_no_path(self): |
| 414 | # If get_filename() raises ImportError then simply skip setting the path |
| 415 | # on the code object. |
| 416 | source_mock_context, filename_mock_context = self.mock_methods( |
| 417 | get_source=True, get_filename=True) |
| 418 | with source_mock_context as source_mock, filename_mock_context as name_mock: |
| 419 | source_mock.return_value = 'attr = 42' |
| 420 | name_mock.side_effect = ImportError |
| 421 | loader = ExecutionLoaderSubclass() |
| 422 | code = loader.get_code('blah') |
| 423 | self.assertEqual(code.co_filename, '<string>') |
Brett Cannon | ef88802 | 2013-06-15 18:39:21 -0400 | [diff] [blame] | 424 | module = types.ModuleType('blah') |
Brett Cannon | 3b62ca8 | 2013-05-27 21:11:04 -0400 | [diff] [blame] | 425 | exec(code, module.__dict__) |
| 426 | self.assertEqual(module.attr, 42) |
| 427 | |
| 428 | |
Brett Cannon | 0dbb4c7 | 2013-05-31 18:56:47 -0400 | [diff] [blame] | 429 | class ExecutionLoaderInitModuleTests(unittest.TestCase): |
| 430 | |
| 431 | @staticmethod |
| 432 | @contextlib.contextmanager |
| 433 | def mock_methods(is_package, filename): |
| 434 | is_package_manager = InspectLoaderInitModuleTests.mock_is_package(is_package) |
| 435 | get_filename_manager = mock.patch.object(ExecutionLoaderSubclass, |
| 436 | 'get_filename', return_value=filename) |
| 437 | with is_package_manager as mock_is_package: |
| 438 | with get_filename_manager as mock_get_filename: |
| 439 | yield {'is_package': mock_is_package, |
| 440 | 'get_filename': mock_get_filename} |
| 441 | |
| 442 | def test_toplevel(self): |
| 443 | # Verify __loader__, __file__, and __package__; no __path__. |
| 444 | name = 'blah' |
| 445 | path = os.path.join('some', 'path', '{}.py'.format(name)) |
| 446 | with self.mock_methods(False, path): |
| 447 | loader = ExecutionLoaderSubclass() |
Brett Cannon | ef88802 | 2013-06-15 18:39:21 -0400 | [diff] [blame] | 448 | module = types.ModuleType(name) |
Brett Cannon | 0dbb4c7 | 2013-05-31 18:56:47 -0400 | [diff] [blame] | 449 | loader.init_module_attrs(module) |
| 450 | self.assertIs(module.__loader__, loader) |
| 451 | self.assertEqual(module.__file__, path) |
| 452 | self.assertEqual(module.__package__, '') |
| 453 | self.assertFalse(hasattr(module, '__path__')) |
| 454 | |
| 455 | def test_package(self): |
| 456 | # Verify __loader__, __file__, __package__, and __path__. |
| 457 | name = 'pkg' |
| 458 | path = os.path.join('some', 'pkg', '__init__.py') |
| 459 | with self.mock_methods(True, path): |
| 460 | loader = ExecutionLoaderSubclass() |
Brett Cannon | ef88802 | 2013-06-15 18:39:21 -0400 | [diff] [blame] | 461 | module = types.ModuleType(name) |
Brett Cannon | 0dbb4c7 | 2013-05-31 18:56:47 -0400 | [diff] [blame] | 462 | loader.init_module_attrs(module) |
| 463 | self.assertIs(module.__loader__, loader) |
| 464 | self.assertEqual(module.__file__, path) |
| 465 | self.assertEqual(module.__package__, 'pkg') |
| 466 | self.assertEqual(module.__path__, [os.path.dirname(path)]) |
| 467 | |
| 468 | def test_submodule(self): |
| 469 | # Verify __package__ and not __path__; test_toplevel() takes care of |
| 470 | # other attributes. |
| 471 | name = 'pkg.submodule' |
| 472 | path = os.path.join('some', 'pkg', 'submodule.py') |
| 473 | with self.mock_methods(False, path): |
| 474 | loader = ExecutionLoaderSubclass() |
Brett Cannon | ef88802 | 2013-06-15 18:39:21 -0400 | [diff] [blame] | 475 | module = types.ModuleType(name) |
Brett Cannon | 0dbb4c7 | 2013-05-31 18:56:47 -0400 | [diff] [blame] | 476 | loader.init_module_attrs(module) |
| 477 | self.assertEqual(module.__package__, 'pkg') |
| 478 | self.assertEqual(module.__file__, path) |
| 479 | self.assertFalse(hasattr(module, '__path__')) |
| 480 | |
| 481 | def test_get_filename_ImportError(self): |
| 482 | # If get_filename() raises ImportError, don't set __file__. |
| 483 | name = 'blah' |
| 484 | path = 'blah.py' |
| 485 | with self.mock_methods(False, path) as mocked_methods: |
| 486 | mocked_methods['get_filename'].side_effect = ImportError |
| 487 | loader = ExecutionLoaderSubclass() |
Brett Cannon | ef88802 | 2013-06-15 18:39:21 -0400 | [diff] [blame] | 488 | module = types.ModuleType(name) |
Brett Cannon | 0dbb4c7 | 2013-05-31 18:56:47 -0400 | [diff] [blame] | 489 | loader.init_module_attrs(module) |
| 490 | self.assertFalse(hasattr(module, '__file__')) |
| 491 | |
Brett Cannon | 3b62ca8 | 2013-05-27 21:11:04 -0400 | [diff] [blame] | 492 | |
Brett Cannon | 9ffe85e | 2013-05-26 16:45:10 -0400 | [diff] [blame] | 493 | ##### SourceLoader concrete methods ############################################ |
Brett Cannon | 100883f | 2013-04-09 16:59:39 -0400 | [diff] [blame] | 494 | class SourceOnlyLoaderMock(abc.SourceLoader): |
| 495 | |
| 496 | # Globals that should be defined for all modules. |
| 497 | source = (b"_ = '::'.join([__name__, __file__, __cached__, __package__, " |
| 498 | b"repr(__loader__)])") |
| 499 | |
| 500 | def __init__(self, path): |
| 501 | self.path = path |
| 502 | |
| 503 | def get_data(self, path): |
| 504 | if path != self.path: |
| 505 | raise IOError |
| 506 | return self.source |
| 507 | |
| 508 | def get_filename(self, fullname): |
| 509 | return self.path |
| 510 | |
| 511 | def module_repr(self, module): |
| 512 | return '<module>' |
| 513 | |
| 514 | |
| 515 | class SourceLoaderMock(SourceOnlyLoaderMock): |
| 516 | |
| 517 | source_mtime = 1 |
| 518 | |
Brett Cannon | ef88802 | 2013-06-15 18:39:21 -0400 | [diff] [blame] | 519 | def __init__(self, path, magic=importlib.util.MAGIC_NUMBER): |
Brett Cannon | 100883f | 2013-04-09 16:59:39 -0400 | [diff] [blame] | 520 | super().__init__(path) |
Brett Cannon | ef88802 | 2013-06-15 18:39:21 -0400 | [diff] [blame] | 521 | self.bytecode_path = importlib.util.cache_from_source(self.path) |
Brett Cannon | 100883f | 2013-04-09 16:59:39 -0400 | [diff] [blame] | 522 | self.source_size = len(self.source) |
| 523 | data = bytearray(magic) |
| 524 | data.extend(importlib._w_long(self.source_mtime)) |
| 525 | data.extend(importlib._w_long(self.source_size)) |
| 526 | code_object = compile(self.source, self.path, 'exec', |
| 527 | dont_inherit=True) |
| 528 | data.extend(marshal.dumps(code_object)) |
| 529 | self.bytecode = bytes(data) |
| 530 | self.written = {} |
| 531 | |
| 532 | def get_data(self, path): |
| 533 | if path == self.path: |
| 534 | return super().get_data(path) |
| 535 | elif path == self.bytecode_path: |
| 536 | return self.bytecode |
| 537 | else: |
| 538 | raise OSError |
| 539 | |
| 540 | def path_stats(self, path): |
| 541 | if path != self.path: |
| 542 | raise IOError |
| 543 | return {'mtime': self.source_mtime, 'size': self.source_size} |
| 544 | |
| 545 | def set_data(self, path, data): |
| 546 | self.written[path] = bytes(data) |
| 547 | return path == self.bytecode_path |
| 548 | |
| 549 | |
| 550 | class SourceLoaderTestHarness(unittest.TestCase): |
| 551 | |
| 552 | def setUp(self, *, is_package=True, **kwargs): |
| 553 | self.package = 'pkg' |
| 554 | if is_package: |
| 555 | self.path = os.path.join(self.package, '__init__.py') |
| 556 | self.name = self.package |
| 557 | else: |
| 558 | module_name = 'mod' |
| 559 | self.path = os.path.join(self.package, '.'.join(['mod', 'py'])) |
| 560 | self.name = '.'.join([self.package, module_name]) |
Brett Cannon | ef88802 | 2013-06-15 18:39:21 -0400 | [diff] [blame] | 561 | self.cached = importlib.util.cache_from_source(self.path) |
Brett Cannon | 100883f | 2013-04-09 16:59:39 -0400 | [diff] [blame] | 562 | self.loader = self.loader_mock(self.path, **kwargs) |
| 563 | |
| 564 | def verify_module(self, module): |
| 565 | self.assertEqual(module.__name__, self.name) |
| 566 | self.assertEqual(module.__file__, self.path) |
| 567 | self.assertEqual(module.__cached__, self.cached) |
| 568 | self.assertEqual(module.__package__, self.package) |
| 569 | self.assertEqual(module.__loader__, self.loader) |
| 570 | values = module._.split('::') |
| 571 | self.assertEqual(values[0], self.name) |
| 572 | self.assertEqual(values[1], self.path) |
| 573 | self.assertEqual(values[2], self.cached) |
| 574 | self.assertEqual(values[3], self.package) |
| 575 | self.assertEqual(values[4], repr(self.loader)) |
| 576 | |
| 577 | def verify_code(self, code_object): |
Brett Cannon | ef88802 | 2013-06-15 18:39:21 -0400 | [diff] [blame] | 578 | module = types.ModuleType(self.name) |
Brett Cannon | 100883f | 2013-04-09 16:59:39 -0400 | [diff] [blame] | 579 | module.__file__ = self.path |
| 580 | module.__cached__ = self.cached |
| 581 | module.__package__ = self.package |
| 582 | module.__loader__ = self.loader |
| 583 | module.__path__ = [] |
| 584 | exec(code_object, module.__dict__) |
| 585 | self.verify_module(module) |
| 586 | |
| 587 | |
| 588 | class SourceOnlyLoaderTests(SourceLoaderTestHarness): |
| 589 | |
| 590 | """Test importlib.abc.SourceLoader for source-only loading. |
| 591 | |
| 592 | Reload testing is subsumed by the tests for |
| 593 | importlib.util.module_for_loader. |
| 594 | |
| 595 | """ |
| 596 | |
| 597 | loader_mock = SourceOnlyLoaderMock |
| 598 | |
| 599 | def test_get_source(self): |
| 600 | # Verify the source code is returned as a string. |
| 601 | # If an OSError is raised by get_data then raise ImportError. |
| 602 | expected_source = self.loader.source.decode('utf-8') |
| 603 | self.assertEqual(self.loader.get_source(self.name), expected_source) |
| 604 | def raise_OSError(path): |
| 605 | raise OSError |
| 606 | self.loader.get_data = raise_OSError |
| 607 | with self.assertRaises(ImportError) as cm: |
| 608 | self.loader.get_source(self.name) |
| 609 | self.assertEqual(cm.exception.name, self.name) |
| 610 | |
| 611 | def test_is_package(self): |
| 612 | # Properly detect when loading a package. |
| 613 | self.setUp(is_package=False) |
| 614 | self.assertFalse(self.loader.is_package(self.name)) |
| 615 | self.setUp(is_package=True) |
| 616 | self.assertTrue(self.loader.is_package(self.name)) |
| 617 | self.assertFalse(self.loader.is_package(self.name + '.__init__')) |
| 618 | |
| 619 | def test_get_code(self): |
| 620 | # Verify the code object is created. |
| 621 | code_object = self.loader.get_code(self.name) |
| 622 | self.verify_code(code_object) |
| 623 | |
| 624 | def test_source_to_code(self): |
| 625 | # Verify the compiled code object. |
| 626 | code = self.loader.source_to_code(self.loader.source, self.path) |
| 627 | self.verify_code(code) |
| 628 | |
| 629 | def test_load_module(self): |
| 630 | # Loading a module should set __name__, __loader__, __package__, |
| 631 | # __path__ (for packages), __file__, and __cached__. |
| 632 | # The module should also be put into sys.modules. |
| 633 | with util.uncache(self.name): |
| 634 | module = self.loader.load_module(self.name) |
| 635 | self.verify_module(module) |
| 636 | self.assertEqual(module.__path__, [os.path.dirname(self.path)]) |
| 637 | self.assertIn(self.name, sys.modules) |
| 638 | |
| 639 | def test_package_settings(self): |
| 640 | # __package__ needs to be set, while __path__ is set on if the module |
| 641 | # is a package. |
| 642 | # Testing the values for a package are covered by test_load_module. |
| 643 | self.setUp(is_package=False) |
| 644 | with util.uncache(self.name): |
| 645 | module = self.loader.load_module(self.name) |
| 646 | self.verify_module(module) |
| 647 | self.assertTrue(not hasattr(module, '__path__')) |
| 648 | |
| 649 | def test_get_source_encoding(self): |
| 650 | # Source is considered encoded in UTF-8 by default unless otherwise |
| 651 | # specified by an encoding line. |
| 652 | source = "_ = 'ü'" |
| 653 | self.loader.source = source.encode('utf-8') |
| 654 | returned_source = self.loader.get_source(self.name) |
| 655 | self.assertEqual(returned_source, source) |
| 656 | source = "# coding: latin-1\n_ = ü" |
| 657 | self.loader.source = source.encode('latin-1') |
| 658 | returned_source = self.loader.get_source(self.name) |
| 659 | self.assertEqual(returned_source, source) |
| 660 | |
| 661 | |
| 662 | @unittest.skipIf(sys.dont_write_bytecode, "sys.dont_write_bytecode is true") |
| 663 | class SourceLoaderBytecodeTests(SourceLoaderTestHarness): |
| 664 | |
| 665 | """Test importlib.abc.SourceLoader's use of bytecode. |
| 666 | |
| 667 | Source-only testing handled by SourceOnlyLoaderTests. |
| 668 | |
| 669 | """ |
| 670 | |
| 671 | loader_mock = SourceLoaderMock |
| 672 | |
| 673 | def verify_code(self, code_object, *, bytecode_written=False): |
| 674 | super().verify_code(code_object) |
| 675 | if bytecode_written: |
| 676 | self.assertIn(self.cached, self.loader.written) |
Brett Cannon | ef88802 | 2013-06-15 18:39:21 -0400 | [diff] [blame] | 677 | data = bytearray(importlib.util.MAGIC_NUMBER) |
Brett Cannon | 100883f | 2013-04-09 16:59:39 -0400 | [diff] [blame] | 678 | data.extend(importlib._w_long(self.loader.source_mtime)) |
| 679 | data.extend(importlib._w_long(self.loader.source_size)) |
| 680 | data.extend(marshal.dumps(code_object)) |
| 681 | self.assertEqual(self.loader.written[self.cached], bytes(data)) |
| 682 | |
| 683 | def test_code_with_everything(self): |
| 684 | # When everything should work. |
| 685 | code_object = self.loader.get_code(self.name) |
| 686 | self.verify_code(code_object) |
| 687 | |
| 688 | def test_no_bytecode(self): |
| 689 | # If no bytecode exists then move on to the source. |
| 690 | self.loader.bytecode_path = "<does not exist>" |
| 691 | # Sanity check |
| 692 | with self.assertRaises(OSError): |
Brett Cannon | ef88802 | 2013-06-15 18:39:21 -0400 | [diff] [blame] | 693 | bytecode_path = importlib.util.cache_from_source(self.path) |
Brett Cannon | 100883f | 2013-04-09 16:59:39 -0400 | [diff] [blame] | 694 | self.loader.get_data(bytecode_path) |
| 695 | code_object = self.loader.get_code(self.name) |
| 696 | self.verify_code(code_object, bytecode_written=True) |
| 697 | |
| 698 | def test_code_bad_timestamp(self): |
| 699 | # Bytecode is only used when the timestamp matches the source EXACTLY. |
| 700 | for source_mtime in (0, 2): |
| 701 | assert source_mtime != self.loader.source_mtime |
| 702 | original = self.loader.source_mtime |
| 703 | self.loader.source_mtime = source_mtime |
| 704 | # If bytecode is used then EOFError would be raised by marshal. |
| 705 | self.loader.bytecode = self.loader.bytecode[8:] |
| 706 | code_object = self.loader.get_code(self.name) |
| 707 | self.verify_code(code_object, bytecode_written=True) |
| 708 | self.loader.source_mtime = original |
| 709 | |
| 710 | def test_code_bad_magic(self): |
| 711 | # Skip over bytecode with a bad magic number. |
| 712 | self.setUp(magic=b'0000') |
| 713 | # If bytecode is used then EOFError would be raised by marshal. |
| 714 | self.loader.bytecode = self.loader.bytecode[8:] |
| 715 | code_object = self.loader.get_code(self.name) |
| 716 | self.verify_code(code_object, bytecode_written=True) |
| 717 | |
| 718 | def test_dont_write_bytecode(self): |
| 719 | # Bytecode is not written if sys.dont_write_bytecode is true. |
| 720 | # Can assume it is false already thanks to the skipIf class decorator. |
| 721 | try: |
| 722 | sys.dont_write_bytecode = True |
| 723 | self.loader.bytecode_path = "<does not exist>" |
| 724 | code_object = self.loader.get_code(self.name) |
| 725 | self.assertNotIn(self.cached, self.loader.written) |
| 726 | finally: |
| 727 | sys.dont_write_bytecode = False |
| 728 | |
| 729 | def test_no_set_data(self): |
| 730 | # If set_data is not defined, one can still read bytecode. |
| 731 | self.setUp(magic=b'0000') |
| 732 | original_set_data = self.loader.__class__.set_data |
| 733 | try: |
| 734 | del self.loader.__class__.set_data |
| 735 | code_object = self.loader.get_code(self.name) |
| 736 | self.verify_code(code_object) |
| 737 | finally: |
| 738 | self.loader.__class__.set_data = original_set_data |
| 739 | |
| 740 | def test_set_data_raises_exceptions(self): |
| 741 | # Raising NotImplementedError or OSError is okay for set_data. |
| 742 | def raise_exception(exc): |
| 743 | def closure(*args, **kwargs): |
| 744 | raise exc |
| 745 | return closure |
| 746 | |
| 747 | self.setUp(magic=b'0000') |
| 748 | self.loader.set_data = raise_exception(NotImplementedError) |
| 749 | code_object = self.loader.get_code(self.name) |
| 750 | self.verify_code(code_object) |
| 751 | |
| 752 | |
| 753 | class SourceLoaderGetSourceTests(unittest.TestCase): |
| 754 | |
| 755 | """Tests for importlib.abc.SourceLoader.get_source().""" |
| 756 | |
| 757 | def test_default_encoding(self): |
| 758 | # Should have no problems with UTF-8 text. |
| 759 | name = 'mod' |
| 760 | mock = SourceOnlyLoaderMock('mod.file') |
| 761 | source = 'x = "ü"' |
| 762 | mock.source = source.encode('utf-8') |
| 763 | returned_source = mock.get_source(name) |
| 764 | self.assertEqual(returned_source, source) |
| 765 | |
| 766 | def test_decoded_source(self): |
| 767 | # Decoding should work. |
| 768 | name = 'mod' |
| 769 | mock = SourceOnlyLoaderMock("mod.file") |
| 770 | source = "# coding: Latin-1\nx='ü'" |
| 771 | assert source.encode('latin-1') != source.encode('utf-8') |
| 772 | mock.source = source.encode('latin-1') |
| 773 | returned_source = mock.get_source(name) |
| 774 | self.assertEqual(returned_source, source) |
| 775 | |
| 776 | def test_universal_newlines(self): |
| 777 | # PEP 302 says universal newlines should be used. |
| 778 | name = 'mod' |
| 779 | mock = SourceOnlyLoaderMock('mod.file') |
| 780 | source = "x = 42\r\ny = -13\r\n" |
| 781 | mock.source = source.encode('utf-8') |
| 782 | expect = io.IncrementalNewlineDecoder(None, True).decode(source) |
| 783 | self.assertEqual(mock.get_source(name), expect) |
Brett Cannon | 2a922ed | 2009-03-09 03:35:50 +0000 | [diff] [blame] | 784 | |
| 785 | |
Brett Cannon | 0dbb4c7 | 2013-05-31 18:56:47 -0400 | [diff] [blame] | 786 | class SourceLoaderInitModuleAttrTests(unittest.TestCase): |
| 787 | |
| 788 | """Tests for importlib.abc.SourceLoader.init_module_attrs().""" |
| 789 | |
| 790 | def test_init_module_attrs(self): |
Brett Cannon | ef88802 | 2013-06-15 18:39:21 -0400 | [diff] [blame] | 791 | # If __file__ set, __cached__ == importlib.util.cached_from_source(__file__). |
Brett Cannon | 0dbb4c7 | 2013-05-31 18:56:47 -0400 | [diff] [blame] | 792 | name = 'blah' |
| 793 | path = 'blah.py' |
| 794 | loader = SourceOnlyLoaderMock(path) |
Brett Cannon | ef88802 | 2013-06-15 18:39:21 -0400 | [diff] [blame] | 795 | module = types.ModuleType(name) |
Brett Cannon | 0dbb4c7 | 2013-05-31 18:56:47 -0400 | [diff] [blame] | 796 | loader.init_module_attrs(module) |
| 797 | self.assertEqual(module.__loader__, loader) |
| 798 | self.assertEqual(module.__package__, '') |
| 799 | self.assertEqual(module.__file__, path) |
Brett Cannon | ef88802 | 2013-06-15 18:39:21 -0400 | [diff] [blame] | 800 | self.assertEqual(module.__cached__, importlib.util.cache_from_source(path)) |
Brett Cannon | 0dbb4c7 | 2013-05-31 18:56:47 -0400 | [diff] [blame] | 801 | |
| 802 | @mock.patch('importlib._bootstrap.cache_from_source') |
| 803 | def test_cache_from_source_NotImplementedError(self, mock_cache_from_source): |
Brett Cannon | ef88802 | 2013-06-15 18:39:21 -0400 | [diff] [blame] | 804 | # If importlib.util.cache_from_source() raises NotImplementedError don't set |
Brett Cannon | 0dbb4c7 | 2013-05-31 18:56:47 -0400 | [diff] [blame] | 805 | # __cached__. |
| 806 | mock_cache_from_source.side_effect = NotImplementedError |
| 807 | name = 'blah' |
| 808 | path = 'blah.py' |
| 809 | loader = SourceOnlyLoaderMock(path) |
Brett Cannon | ef88802 | 2013-06-15 18:39:21 -0400 | [diff] [blame] | 810 | module = types.ModuleType(name) |
Brett Cannon | 0dbb4c7 | 2013-05-31 18:56:47 -0400 | [diff] [blame] | 811 | loader.init_module_attrs(module) |
| 812 | self.assertEqual(module.__file__, path) |
| 813 | self.assertFalse(hasattr(module, '__cached__')) |
| 814 | |
| 815 | def test_no_get_filename(self): |
| 816 | # No __file__, no __cached__. |
| 817 | with mock.patch.object(SourceOnlyLoaderMock, 'get_filename') as mocked: |
| 818 | mocked.side_effect = ImportError |
| 819 | name = 'blah' |
| 820 | loader = SourceOnlyLoaderMock('blah.py') |
Brett Cannon | ef88802 | 2013-06-15 18:39:21 -0400 | [diff] [blame] | 821 | module = types.ModuleType(name) |
Brett Cannon | 0dbb4c7 | 2013-05-31 18:56:47 -0400 | [diff] [blame] | 822 | loader.init_module_attrs(module) |
| 823 | self.assertFalse(hasattr(module, '__file__')) |
| 824 | self.assertFalse(hasattr(module, '__cached__')) |
| 825 | |
| 826 | |
Brett Cannon | 9ffe85e | 2013-05-26 16:45:10 -0400 | [diff] [blame] | 827 | |
Brett Cannon | 2a922ed | 2009-03-09 03:35:50 +0000 | [diff] [blame] | 828 | if __name__ == '__main__': |
Brett Cannon | 100883f | 2013-04-09 16:59:39 -0400 | [diff] [blame] | 829 | unittest.main() |