test_abc.py revision 7e70fa53142564bed7415b3798c3e77f0574f816
1import contextlib 2import inspect 3import io 4import marshal 5import os 6import sys 7from test import support 8import types 9import unittest 10from unittest import mock 11 12from . import util 13 14frozen_init, source_init = util.import_importlib('importlib') 15frozen_abc, source_abc = util.import_importlib('importlib.abc') 16frozen_util, source_util = util.import_importlib('importlib.util') 17 18##### Inheritance ############################################################## 19class InheritanceTests: 20 21 """Test that the specified class is a subclass/superclass of the expected 22 classes.""" 23 24 subclasses = [] 25 superclasses = [] 26 27 def __init__(self, *args, **kwargs): 28 super().__init__(*args, **kwargs) 29 self.superclasses = [getattr(self.abc, class_name) 30 for class_name in self.superclass_names] 31 if hasattr(self, 'subclass_names'): 32 # Because test.support.import_fresh_module() creates a new 33 # importlib._bootstrap per module, inheritance checks fail when 34 # checking across module boundaries (i.e. the _bootstrap in abc is 35 # not the same as the one in machinery). That means stealing one of 36 # the modules from the other to make sure the same instance is used. 37 self.subclasses = [getattr(self.abc.machinery, class_name) 38 for class_name in self.subclass_names] 39 assert self.subclasses or self.superclasses, self.__class__ 40 testing = self.__class__.__name__.partition('_')[2] 41 self.__test = getattr(self.abc, testing) 42 43 def test_subclasses(self): 44 # Test that the expected subclasses inherit. 45 for subclass in self.subclasses: 46 self.assertTrue(issubclass(subclass, self.__test), 47 "{0} is not a subclass of {1}".format(subclass, self.__test)) 48 49 def test_superclasses(self): 50 # Test that the class inherits from the expected superclasses. 51 for superclass in self.superclasses: 52 self.assertTrue(issubclass(self.__test, superclass), 53 "{0} is not a superclass of {1}".format(superclass, self.__test)) 54 55def create_inheritance_tests(base_class): 56 def set_frozen(ns): 57 ns['abc'] = frozen_abc 58 def set_source(ns): 59 ns['abc'] = source_abc 60 61 classes = [] 62 for prefix, ns_set in [('Frozen', set_frozen), ('Source', set_source)]: 63 classes.append(types.new_class('_'.join([prefix, base_class.__name__]), 64 (base_class, unittest.TestCase), 65 exec_body=ns_set)) 66 return classes 67 68 69class MetaPathFinder(InheritanceTests): 70 superclass_names = ['Finder'] 71 subclass_names = ['BuiltinImporter', 'FrozenImporter', 'PathFinder', 72 'WindowsRegistryFinder'] 73 74tests = create_inheritance_tests(MetaPathFinder) 75Frozen_MetaPathFinderInheritanceTests, Source_MetaPathFinderInheritanceTests = tests 76 77 78class PathEntryFinder(InheritanceTests): 79 superclass_names = ['Finder'] 80 subclass_names = ['FileFinder'] 81 82tests = create_inheritance_tests(PathEntryFinder) 83Frozen_PathEntryFinderInheritanceTests, Source_PathEntryFinderInheritanceTests = tests 84 85 86class ResourceLoader(InheritanceTests): 87 superclass_names = ['Loader'] 88 89tests = create_inheritance_tests(ResourceLoader) 90Frozen_ResourceLoaderInheritanceTests, Source_ResourceLoaderInheritanceTests = tests 91 92 93class InspectLoader(InheritanceTests): 94 superclass_names = ['Loader'] 95 subclass_names = ['BuiltinImporter', 'FrozenImporter', 'ExtensionFileLoader'] 96 97tests = create_inheritance_tests(InspectLoader) 98Frozen_InspectLoaderInheritanceTests, Source_InspectLoaderInheritanceTests = tests 99 100 101class ExecutionLoader(InheritanceTests): 102 superclass_names = ['InspectLoader'] 103 subclass_names = ['ExtensionFileLoader'] 104 105tests = create_inheritance_tests(ExecutionLoader) 106Frozen_ExecutionLoaderInheritanceTests, Source_ExecutionLoaderInheritanceTests = tests 107 108 109class FileLoader(InheritanceTests): 110 superclass_names = ['ResourceLoader', 'ExecutionLoader'] 111 subclass_names = ['SourceFileLoader', 'SourcelessFileLoader'] 112 113tests = create_inheritance_tests(FileLoader) 114Frozen_FileLoaderInheritanceTests, Source_FileLoaderInheritanceTests = tests 115 116 117class SourceLoader(InheritanceTests): 118 superclass_names = ['ResourceLoader', 'ExecutionLoader'] 119 subclass_names = ['SourceFileLoader'] 120 121tests = create_inheritance_tests(SourceLoader) 122Frozen_SourceLoaderInheritanceTests, Source_SourceLoaderInheritanceTests = tests 123 124##### Default return values #################################################### 125def make_abc_subclasses(base_class): 126 classes = [] 127 for kind, abc in [('Frozen', frozen_abc), ('Source', source_abc)]: 128 name = '_'.join([kind, base_class.__name__]) 129 base_classes = base_class, getattr(abc, base_class.__name__) 130 classes.append(types.new_class(name, base_classes)) 131 return classes 132 133def make_return_value_tests(base_class, test_class): 134 frozen_class, source_class = make_abc_subclasses(base_class) 135 tests = [] 136 for prefix, class_in_test in [('Frozen', frozen_class), ('Source', source_class)]: 137 def set_ns(ns): 138 ns['ins'] = class_in_test() 139 tests.append(types.new_class('_'.join([prefix, test_class.__name__]), 140 (test_class, unittest.TestCase), 141 exec_body=set_ns)) 142 return tests 143 144 145class MetaPathFinder: 146 147 def find_module(self, fullname, path): 148 return super().find_module(fullname, path) 149 150Frozen_MPF, Source_MPF = make_abc_subclasses(MetaPathFinder) 151 152 153class MetaPathFinderDefaultsTests: 154 155 def test_find_module(self): 156 # Default should return None. 157 self.assertIsNone(self.ins.find_module('something', None)) 158 159 def test_invalidate_caches(self): 160 # Calling the method is a no-op. 161 self.ins.invalidate_caches() 162 163 164tests = make_return_value_tests(MetaPathFinder, MetaPathFinderDefaultsTests) 165Frozen_MPFDefaultTests, Source_MPFDefaultTests = tests 166 167 168class PathEntryFinder: 169 170 def find_loader(self, fullname): 171 return super().find_loader(fullname) 172 173Frozen_PEF, Source_PEF = make_abc_subclasses(PathEntryFinder) 174 175 176class PathEntryFinderDefaultsTests: 177 178 def test_find_loader(self): 179 self.assertEqual((None, []), self.ins.find_loader('something')) 180 181 def find_module(self): 182 self.assertEqual(None, self.ins.find_module('something')) 183 184 def test_invalidate_caches(self): 185 # Should be a no-op. 186 self.ins.invalidate_caches() 187 188 189tests = make_return_value_tests(PathEntryFinder, PathEntryFinderDefaultsTests) 190Frozen_PEFDefaultTests, Source_PEFDefaultTests = tests 191 192 193class Loader: 194 195 def load_module(self, fullname): 196 return super().load_module(fullname) 197 198 199Frozen_L, Source_L = make_abc_subclasses(Loader) 200 201 202class LoaderDefaultsTests: 203 204 def test_load_module(self): 205 with self.assertRaises(ImportError): 206 self.ins.load_module('something') 207 208 def test_module_repr(self): 209 mod = types.ModuleType('blah') 210 with self.assertRaises(NotImplementedError): 211 self.ins.module_repr(mod) 212 original_repr = repr(mod) 213 mod.__loader__ = self.ins 214 # Should still return a proper repr. 215 self.assertTrue(repr(mod)) 216 217 218tests = make_return_value_tests(Loader, LoaderDefaultsTests) 219Frozen_LDefaultTests, SourceLDefaultTests = tests 220 221 222class ResourceLoader(Loader): 223 224 def get_data(self, path): 225 return super().get_data(path) 226 227 228Frozen_RL, Source_RL = make_abc_subclasses(ResourceLoader) 229 230 231class ResourceLoaderDefaultsTests: 232 233 def test_get_data(self): 234 with self.assertRaises(IOError): 235 self.ins.get_data('/some/path') 236 237 238tests = make_return_value_tests(ResourceLoader, ResourceLoaderDefaultsTests) 239Frozen_RLDefaultTests, Source_RLDefaultTests = tests 240 241 242class InspectLoader(Loader): 243 244 def is_package(self, fullname): 245 return super().is_package(fullname) 246 247 def get_source(self, fullname): 248 return super().get_source(fullname) 249 250 251Frozen_IL, Source_IL = make_abc_subclasses(InspectLoader) 252 253 254class InspectLoaderDefaultsTests: 255 256 def test_is_package(self): 257 with self.assertRaises(ImportError): 258 self.ins.is_package('blah') 259 260 def test_get_source(self): 261 with self.assertRaises(ImportError): 262 self.ins.get_source('blah') 263 264 265tests = make_return_value_tests(InspectLoader, InspectLoaderDefaultsTests) 266Frozen_ILDefaultTests, Source_ILDefaultTests = tests 267 268 269class ExecutionLoader(InspectLoader): 270 271 def get_filename(self, fullname): 272 return super().get_filename(fullname) 273 274Frozen_EL, Source_EL = make_abc_subclasses(ExecutionLoader) 275 276 277class ExecutionLoaderDefaultsTests: 278 279 def test_get_filename(self): 280 with self.assertRaises(ImportError): 281 self.ins.get_filename('blah') 282 283 284tests = make_return_value_tests(ExecutionLoader, InspectLoaderDefaultsTests) 285Frozen_ELDefaultTests, Source_ELDefaultsTests = tests 286 287##### Loader concrete methods ################################################## 288class LoaderConcreteMethodTests: 289 290 def test_init_module_attrs(self): 291 loader = self.LoaderSubclass() 292 module = types.ModuleType('blah') 293 loader.init_module_attrs(module) 294 self.assertEqual(module.__loader__, loader) 295 296 297class Frozen_LoaderConcreateMethodTests(LoaderConcreteMethodTests, unittest.TestCase): 298 LoaderSubclass = Frozen_L 299 300class Source_LoaderConcreateMethodTests(LoaderConcreteMethodTests, unittest.TestCase): 301 LoaderSubclass = Source_L 302 303 304##### InspectLoader concrete methods ########################################### 305class InspectLoaderSourceToCodeTests: 306 307 def source_to_module(self, data, path=None): 308 """Help with source_to_code() tests.""" 309 module = types.ModuleType('blah') 310 loader = self.InspectLoaderSubclass() 311 if path is None: 312 code = loader.source_to_code(data) 313 else: 314 code = loader.source_to_code(data, path) 315 exec(code, module.__dict__) 316 return module 317 318 def test_source_to_code_source(self): 319 # Since compile() can handle strings, so should source_to_code(). 320 source = 'attr = 42' 321 module = self.source_to_module(source) 322 self.assertTrue(hasattr(module, 'attr')) 323 self.assertEqual(module.attr, 42) 324 325 def test_source_to_code_bytes(self): 326 # Since compile() can handle bytes, so should source_to_code(). 327 source = b'attr = 42' 328 module = self.source_to_module(source) 329 self.assertTrue(hasattr(module, 'attr')) 330 self.assertEqual(module.attr, 42) 331 332 def test_source_to_code_path(self): 333 # Specifying a path should set it for the code object. 334 path = 'path/to/somewhere' 335 loader = self.InspectLoaderSubclass() 336 code = loader.source_to_code('', path) 337 self.assertEqual(code.co_filename, path) 338 339 def test_source_to_code_no_path(self): 340 # Not setting a path should still work and be set to <string> since that 341 # is a pre-existing practice as a default to compile(). 342 loader = self.InspectLoaderSubclass() 343 code = loader.source_to_code('') 344 self.assertEqual(code.co_filename, '<string>') 345 346 347class Frozen_ILSourceToCodeTests(InspectLoaderSourceToCodeTests, unittest.TestCase): 348 InspectLoaderSubclass = Frozen_IL 349 350class Source_ILSourceToCodeTests(InspectLoaderSourceToCodeTests, unittest.TestCase): 351 InspectLoaderSubclass = Source_IL 352 353 354class InspectLoaderGetCodeTests: 355 356 def test_get_code(self): 357 # Test success. 358 module = types.ModuleType('blah') 359 with mock.patch.object(self.InspectLoaderSubclass, 'get_source') as mocked: 360 mocked.return_value = 'attr = 42' 361 loader = self.InspectLoaderSubclass() 362 code = loader.get_code('blah') 363 exec(code, module.__dict__) 364 self.assertEqual(module.attr, 42) 365 366 def test_get_code_source_is_None(self): 367 # If get_source() is None then this should be None. 368 with mock.patch.object(self.InspectLoaderSubclass, 'get_source') as mocked: 369 mocked.return_value = None 370 loader = self.InspectLoaderSubclass() 371 code = loader.get_code('blah') 372 self.assertIsNone(code) 373 374 def test_get_code_source_not_found(self): 375 # If there is no source then there is no code object. 376 loader = self.InspectLoaderSubclass() 377 with self.assertRaises(ImportError): 378 loader.get_code('blah') 379 380 381class Frozen_ILGetCodeTests(InspectLoaderGetCodeTests, unittest.TestCase): 382 InspectLoaderSubclass = Frozen_IL 383 384class Source_ILGetCodeTests(InspectLoaderGetCodeTests, unittest.TestCase): 385 InspectLoaderSubclass = Source_IL 386 387 388class InspectLoaderInitModuleTests: 389 390 def mock_is_package(self, return_value): 391 return mock.patch.object(self.InspectLoaderSubclass, 'is_package', 392 return_value=return_value) 393 394 def init_module_attrs(self, name): 395 loader = self.InspectLoaderSubclass() 396 module = types.ModuleType(name) 397 loader.init_module_attrs(module) 398 self.assertEqual(module.__loader__, loader) 399 return module 400 401 def test_package(self): 402 # If a package, then __package__ == __name__, __path__ == [] 403 with self.mock_is_package(True): 404 name = 'blah' 405 module = self.init_module_attrs(name) 406 self.assertEqual(module.__package__, name) 407 self.assertEqual(module.__path__, []) 408 409 def test_toplevel(self): 410 # If a module is top-level, __package__ == '' 411 with self.mock_is_package(False): 412 name = 'blah' 413 module = self.init_module_attrs(name) 414 self.assertEqual(module.__package__, '') 415 416 def test_submodule(self): 417 # If a module is contained within a package then set __package__ to the 418 # package name. 419 with self.mock_is_package(False): 420 name = 'pkg.mod' 421 module = self.init_module_attrs(name) 422 self.assertEqual(module.__package__, 'pkg') 423 424 def test_is_package_ImportError(self): 425 # If is_package() raises ImportError, __package__ should be None and 426 # __path__ should not be set. 427 with self.mock_is_package(False) as mocked_method: 428 mocked_method.side_effect = ImportError 429 name = 'mod' 430 module = self.init_module_attrs(name) 431 self.assertIsNone(module.__package__) 432 self.assertFalse(hasattr(module, '__path__')) 433 434 435class Frozen_ILInitModuleTests(InspectLoaderInitModuleTests, unittest.TestCase): 436 InspectLoaderSubclass = Frozen_IL 437 438class Source_ILInitModuleTests(InspectLoaderInitModuleTests, unittest.TestCase): 439 InspectLoaderSubclass = Source_IL 440 441 442class InspectLoaderLoadModuleTests: 443 444 """Test InspectLoader.load_module().""" 445 446 module_name = 'blah' 447 448 def setUp(self): 449 support.unload(self.module_name) 450 self.addCleanup(support.unload, self.module_name) 451 452 def mock_get_code(self): 453 return mock.patch.object(self.InspectLoaderSubclass, 'get_code') 454 455 def test_get_code_ImportError(self): 456 # If get_code() raises ImportError, it should propagate. 457 with self.mock_get_code() as mocked_get_code: 458 mocked_get_code.side_effect = ImportError 459 with self.assertRaises(ImportError): 460 loader = self.InspectLoaderSubclass() 461 loader.load_module(self.module_name) 462 463 def test_get_code_None(self): 464 # If get_code() returns None, raise ImportError. 465 with self.mock_get_code() as mocked_get_code: 466 mocked_get_code.return_value = None 467 with self.assertRaises(ImportError): 468 loader = self.InspectLoaderSubclass() 469 loader.load_module(self.module_name) 470 471 def test_module_returned(self): 472 # The loaded module should be returned. 473 code = compile('attr = 42', '<string>', 'exec') 474 with self.mock_get_code() as mocked_get_code: 475 mocked_get_code.return_value = code 476 loader = self.InspectLoaderSubclass() 477 module = loader.load_module(self.module_name) 478 self.assertEqual(module, sys.modules[self.module_name]) 479 480 481class Frozen_ILLoadModuleTests(InspectLoaderLoadModuleTests, unittest.TestCase): 482 InspectLoaderSubclass = Frozen_IL 483 484class Source_ILLoadModuleTests(InspectLoaderLoadModuleTests, unittest.TestCase): 485 InspectLoaderSubclass = Source_IL 486 487 488##### ExecutionLoader concrete methods ######################################### 489class ExecutionLoaderGetCodeTests: 490 491 def mock_methods(self, *, get_source=False, get_filename=False): 492 source_mock_context, filename_mock_context = None, None 493 if get_source: 494 source_mock_context = mock.patch.object(self.ExecutionLoaderSubclass, 495 'get_source') 496 if get_filename: 497 filename_mock_context = mock.patch.object(self.ExecutionLoaderSubclass, 498 'get_filename') 499 return source_mock_context, filename_mock_context 500 501 def test_get_code(self): 502 path = 'blah.py' 503 source_mock_context, filename_mock_context = self.mock_methods( 504 get_source=True, get_filename=True) 505 with source_mock_context as source_mock, filename_mock_context as name_mock: 506 source_mock.return_value = 'attr = 42' 507 name_mock.return_value = path 508 loader = self.ExecutionLoaderSubclass() 509 code = loader.get_code('blah') 510 self.assertEqual(code.co_filename, path) 511 module = types.ModuleType('blah') 512 exec(code, module.__dict__) 513 self.assertEqual(module.attr, 42) 514 515 def test_get_code_source_is_None(self): 516 # If get_source() is None then this should be None. 517 source_mock_context, _ = self.mock_methods(get_source=True) 518 with source_mock_context as mocked: 519 mocked.return_value = None 520 loader = self.ExecutionLoaderSubclass() 521 code = loader.get_code('blah') 522 self.assertIsNone(code) 523 524 def test_get_code_source_not_found(self): 525 # If there is no source then there is no code object. 526 loader = self.ExecutionLoaderSubclass() 527 with self.assertRaises(ImportError): 528 loader.get_code('blah') 529 530 def test_get_code_no_path(self): 531 # If get_filename() raises ImportError then simply skip setting the path 532 # on the code object. 533 source_mock_context, filename_mock_context = self.mock_methods( 534 get_source=True, get_filename=True) 535 with source_mock_context as source_mock, filename_mock_context as name_mock: 536 source_mock.return_value = 'attr = 42' 537 name_mock.side_effect = ImportError 538 loader = self.ExecutionLoaderSubclass() 539 code = loader.get_code('blah') 540 self.assertEqual(code.co_filename, '<string>') 541 module = types.ModuleType('blah') 542 exec(code, module.__dict__) 543 self.assertEqual(module.attr, 42) 544 545 546class Frozen_ELGetCodeTests(ExecutionLoaderGetCodeTests, unittest.TestCase): 547 ExecutionLoaderSubclass = Frozen_EL 548 549class Source_ELGetCodeTests(ExecutionLoaderGetCodeTests, unittest.TestCase): 550 ExecutionLoaderSubclass = Source_EL 551 552 553class ExecutionLoaderInitModuleTests: 554 555 def mock_is_package(self, return_value): 556 return mock.patch.object(self.ExecutionLoaderSubclass, 'is_package', 557 return_value=return_value) 558 559 @contextlib.contextmanager 560 def mock_methods(self, is_package, filename): 561 is_package_manager = self.mock_is_package(is_package) 562 get_filename_manager = mock.patch.object(self.ExecutionLoaderSubclass, 563 'get_filename', return_value=filename) 564 with is_package_manager as mock_is_package: 565 with get_filename_manager as mock_get_filename: 566 yield {'is_package': mock_is_package, 567 'get_filename': mock_get_filename} 568 569 def test_toplevel(self): 570 # Verify __loader__, __file__, and __package__; no __path__. 571 name = 'blah' 572 path = os.path.join('some', 'path', '{}.py'.format(name)) 573 with self.mock_methods(False, path): 574 loader = self.ExecutionLoaderSubclass() 575 module = types.ModuleType(name) 576 loader.init_module_attrs(module) 577 self.assertIs(module.__loader__, loader) 578 self.assertEqual(module.__file__, path) 579 self.assertEqual(module.__package__, '') 580 self.assertFalse(hasattr(module, '__path__')) 581 582 def test_package(self): 583 # Verify __loader__, __file__, __package__, and __path__. 584 name = 'pkg' 585 path = os.path.join('some', 'pkg', '__init__.py') 586 with self.mock_methods(True, path): 587 loader = self.ExecutionLoaderSubclass() 588 module = types.ModuleType(name) 589 loader.init_module_attrs(module) 590 self.assertIs(module.__loader__, loader) 591 self.assertEqual(module.__file__, path) 592 self.assertEqual(module.__package__, 'pkg') 593 self.assertEqual(module.__path__, [os.path.dirname(path)]) 594 595 def test_submodule(self): 596 # Verify __package__ and not __path__; test_toplevel() takes care of 597 # other attributes. 598 name = 'pkg.submodule' 599 path = os.path.join('some', 'pkg', 'submodule.py') 600 with self.mock_methods(False, path): 601 loader = self.ExecutionLoaderSubclass() 602 module = types.ModuleType(name) 603 loader.init_module_attrs(module) 604 self.assertEqual(module.__package__, 'pkg') 605 self.assertEqual(module.__file__, path) 606 self.assertFalse(hasattr(module, '__path__')) 607 608 def test_get_filename_ImportError(self): 609 # If get_filename() raises ImportError, don't set __file__. 610 name = 'blah' 611 path = 'blah.py' 612 with self.mock_methods(False, path) as mocked_methods: 613 mocked_methods['get_filename'].side_effect = ImportError 614 loader = self.ExecutionLoaderSubclass() 615 module = types.ModuleType(name) 616 loader.init_module_attrs(module) 617 self.assertFalse(hasattr(module, '__file__')) 618 619 620class Frozen_ELInitModuleTests(ExecutionLoaderInitModuleTests, unittest.TestCase): 621 ExecutionLoaderSubclass = Frozen_EL 622 623class Source_ELInitModuleTests(ExecutionLoaderInitModuleTests, unittest.TestCase): 624 ExecutionLoaderSubclass = Source_EL 625 626 627##### SourceLoader concrete methods ############################################ 628class SourceLoader: 629 630 # Globals that should be defined for all modules. 631 source = (b"_ = '::'.join([__name__, __file__, __cached__, __package__, " 632 b"repr(__loader__)])") 633 634 def __init__(self, path): 635 self.path = path 636 637 def get_data(self, path): 638 if path != self.path: 639 raise IOError 640 return self.source 641 642 def get_filename(self, fullname): 643 return self.path 644 645 def module_repr(self, module): 646 return '<module>' 647 648 649Frozen_SourceOnlyL, Source_SourceOnlyL = make_abc_subclasses(SourceLoader) 650 651 652class SourceLoader(SourceLoader): 653 654 source_mtime = 1 655 656 def __init__(self, path, magic=None): 657 super().__init__(path) 658 self.bytecode_path = self.util.cache_from_source(self.path) 659 self.source_size = len(self.source) 660 if magic is None: 661 magic = self.util.MAGIC_NUMBER 662 data = bytearray(magic) 663 data.extend(self.init._w_long(self.source_mtime)) 664 data.extend(self.init._w_long(self.source_size)) 665 code_object = compile(self.source, self.path, 'exec', 666 dont_inherit=True) 667 data.extend(marshal.dumps(code_object)) 668 self.bytecode = bytes(data) 669 self.written = {} 670 671 def get_data(self, path): 672 if path == self.path: 673 return super().get_data(path) 674 elif path == self.bytecode_path: 675 return self.bytecode 676 else: 677 raise OSError 678 679 def path_stats(self, path): 680 if path != self.path: 681 raise IOError 682 return {'mtime': self.source_mtime, 'size': self.source_size} 683 684 def set_data(self, path, data): 685 self.written[path] = bytes(data) 686 return path == self.bytecode_path 687 688 689Frozen_SL, Source_SL = make_abc_subclasses(SourceLoader) 690Frozen_SL.util = frozen_util 691Source_SL.util = source_util 692Frozen_SL.init = frozen_init 693Source_SL.init = source_init 694 695 696class SourceLoaderTestHarness: 697 698 def setUp(self, *, is_package=True, **kwargs): 699 self.package = 'pkg' 700 if is_package: 701 self.path = os.path.join(self.package, '__init__.py') 702 self.name = self.package 703 else: 704 module_name = 'mod' 705 self.path = os.path.join(self.package, '.'.join(['mod', 'py'])) 706 self.name = '.'.join([self.package, module_name]) 707 self.cached = self.util.cache_from_source(self.path) 708 self.loader = self.loader_mock(self.path, **kwargs) 709 710 def verify_module(self, module): 711 self.assertEqual(module.__name__, self.name) 712 self.assertEqual(module.__file__, self.path) 713 self.assertEqual(module.__cached__, self.cached) 714 self.assertEqual(module.__package__, self.package) 715 self.assertEqual(module.__loader__, self.loader) 716 values = module._.split('::') 717 self.assertEqual(values[0], self.name) 718 self.assertEqual(values[1], self.path) 719 self.assertEqual(values[2], self.cached) 720 self.assertEqual(values[3], self.package) 721 self.assertEqual(values[4], repr(self.loader)) 722 723 def verify_code(self, code_object): 724 module = types.ModuleType(self.name) 725 module.__file__ = self.path 726 module.__cached__ = self.cached 727 module.__package__ = self.package 728 module.__loader__ = self.loader 729 module.__path__ = [] 730 exec(code_object, module.__dict__) 731 self.verify_module(module) 732 733 734class SourceOnlyLoaderTests(SourceLoaderTestHarness): 735 736 """Test importlib.abc.SourceLoader for source-only loading. 737 738 Reload testing is subsumed by the tests for 739 importlib.util.module_for_loader. 740 741 """ 742 743 def test_get_source(self): 744 # Verify the source code is returned as a string. 745 # If an OSError is raised by get_data then raise ImportError. 746 expected_source = self.loader.source.decode('utf-8') 747 self.assertEqual(self.loader.get_source(self.name), expected_source) 748 def raise_OSError(path): 749 raise OSError 750 self.loader.get_data = raise_OSError 751 with self.assertRaises(ImportError) as cm: 752 self.loader.get_source(self.name) 753 self.assertEqual(cm.exception.name, self.name) 754 755 def test_is_package(self): 756 # Properly detect when loading a package. 757 self.setUp(is_package=False) 758 self.assertFalse(self.loader.is_package(self.name)) 759 self.setUp(is_package=True) 760 self.assertTrue(self.loader.is_package(self.name)) 761 self.assertFalse(self.loader.is_package(self.name + '.__init__')) 762 763 def test_get_code(self): 764 # Verify the code object is created. 765 code_object = self.loader.get_code(self.name) 766 self.verify_code(code_object) 767 768 def test_source_to_code(self): 769 # Verify the compiled code object. 770 code = self.loader.source_to_code(self.loader.source, self.path) 771 self.verify_code(code) 772 773 def test_load_module(self): 774 # Loading a module should set __name__, __loader__, __package__, 775 # __path__ (for packages), __file__, and __cached__. 776 # The module should also be put into sys.modules. 777 with util.uncache(self.name): 778 module = self.loader.load_module(self.name) 779 self.verify_module(module) 780 self.assertEqual(module.__path__, [os.path.dirname(self.path)]) 781 self.assertIn(self.name, sys.modules) 782 783 def test_package_settings(self): 784 # __package__ needs to be set, while __path__ is set on if the module 785 # is a package. 786 # Testing the values for a package are covered by test_load_module. 787 self.setUp(is_package=False) 788 with util.uncache(self.name): 789 module = self.loader.load_module(self.name) 790 self.verify_module(module) 791 self.assertTrue(not hasattr(module, '__path__')) 792 793 def test_get_source_encoding(self): 794 # Source is considered encoded in UTF-8 by default unless otherwise 795 # specified by an encoding line. 796 source = "_ = 'ü'" 797 self.loader.source = source.encode('utf-8') 798 returned_source = self.loader.get_source(self.name) 799 self.assertEqual(returned_source, source) 800 source = "# coding: latin-1\n_ = ü" 801 self.loader.source = source.encode('latin-1') 802 returned_source = self.loader.get_source(self.name) 803 self.assertEqual(returned_source, source) 804 805 806class Frozen_SourceOnlyLTests(SourceOnlyLoaderTests, unittest.TestCase): 807 loader_mock = Frozen_SourceOnlyL 808 util = frozen_util 809 810class Source_SourceOnlyLTests(SourceOnlyLoaderTests, unittest.TestCase): 811 loader_mock = Source_SourceOnlyL 812 util = source_util 813 814 815@unittest.skipIf(sys.dont_write_bytecode, "sys.dont_write_bytecode is true") 816class SourceLoaderBytecodeTests(SourceLoaderTestHarness): 817 818 """Test importlib.abc.SourceLoader's use of bytecode. 819 820 Source-only testing handled by SourceOnlyLoaderTests. 821 822 """ 823 824 def verify_code(self, code_object, *, bytecode_written=False): 825 super().verify_code(code_object) 826 if bytecode_written: 827 self.assertIn(self.cached, self.loader.written) 828 data = bytearray(self.util.MAGIC_NUMBER) 829 data.extend(self.init._w_long(self.loader.source_mtime)) 830 data.extend(self.init._w_long(self.loader.source_size)) 831 data.extend(marshal.dumps(code_object)) 832 self.assertEqual(self.loader.written[self.cached], bytes(data)) 833 834 def test_code_with_everything(self): 835 # When everything should work. 836 code_object = self.loader.get_code(self.name) 837 self.verify_code(code_object) 838 839 def test_no_bytecode(self): 840 # If no bytecode exists then move on to the source. 841 self.loader.bytecode_path = "<does not exist>" 842 # Sanity check 843 with self.assertRaises(OSError): 844 bytecode_path = self.util.cache_from_source(self.path) 845 self.loader.get_data(bytecode_path) 846 code_object = self.loader.get_code(self.name) 847 self.verify_code(code_object, bytecode_written=True) 848 849 def test_code_bad_timestamp(self): 850 # Bytecode is only used when the timestamp matches the source EXACTLY. 851 for source_mtime in (0, 2): 852 assert source_mtime != self.loader.source_mtime 853 original = self.loader.source_mtime 854 self.loader.source_mtime = source_mtime 855 # If bytecode is used then EOFError would be raised by marshal. 856 self.loader.bytecode = self.loader.bytecode[8:] 857 code_object = self.loader.get_code(self.name) 858 self.verify_code(code_object, bytecode_written=True) 859 self.loader.source_mtime = original 860 861 def test_code_bad_magic(self): 862 # Skip over bytecode with a bad magic number. 863 self.setUp(magic=b'0000') 864 # If bytecode is used then EOFError would be raised by marshal. 865 self.loader.bytecode = self.loader.bytecode[8:] 866 code_object = self.loader.get_code(self.name) 867 self.verify_code(code_object, bytecode_written=True) 868 869 def test_dont_write_bytecode(self): 870 # Bytecode is not written if sys.dont_write_bytecode is true. 871 # Can assume it is false already thanks to the skipIf class decorator. 872 try: 873 sys.dont_write_bytecode = True 874 self.loader.bytecode_path = "<does not exist>" 875 code_object = self.loader.get_code(self.name) 876 self.assertNotIn(self.cached, self.loader.written) 877 finally: 878 sys.dont_write_bytecode = False 879 880 def test_no_set_data(self): 881 # If set_data is not defined, one can still read bytecode. 882 self.setUp(magic=b'0000') 883 original_set_data = self.loader.__class__.mro()[1].set_data 884 try: 885 del self.loader.__class__.mro()[1].set_data 886 code_object = self.loader.get_code(self.name) 887 self.verify_code(code_object) 888 finally: 889 self.loader.__class__.mro()[1].set_data = original_set_data 890 891 def test_set_data_raises_exceptions(self): 892 # Raising NotImplementedError or OSError is okay for set_data. 893 def raise_exception(exc): 894 def closure(*args, **kwargs): 895 raise exc 896 return closure 897 898 self.setUp(magic=b'0000') 899 self.loader.set_data = raise_exception(NotImplementedError) 900 code_object = self.loader.get_code(self.name) 901 self.verify_code(code_object) 902 903 904class Frozen_SLBytecodeTests(SourceLoaderBytecodeTests, unittest.TestCase): 905 loader_mock = Frozen_SL 906 init = frozen_init 907 util = frozen_util 908 909class SourceSLBytecodeTests(SourceLoaderBytecodeTests, unittest.TestCase): 910 loader_mock = Source_SL 911 init = source_init 912 util = source_util 913 914 915class SourceLoaderGetSourceTests: 916 917 """Tests for importlib.abc.SourceLoader.get_source().""" 918 919 def test_default_encoding(self): 920 # Should have no problems with UTF-8 text. 921 name = 'mod' 922 mock = self.SourceOnlyLoaderMock('mod.file') 923 source = 'x = "ü"' 924 mock.source = source.encode('utf-8') 925 returned_source = mock.get_source(name) 926 self.assertEqual(returned_source, source) 927 928 def test_decoded_source(self): 929 # Decoding should work. 930 name = 'mod' 931 mock = self.SourceOnlyLoaderMock("mod.file") 932 source = "# coding: Latin-1\nx='ü'" 933 assert source.encode('latin-1') != source.encode('utf-8') 934 mock.source = source.encode('latin-1') 935 returned_source = mock.get_source(name) 936 self.assertEqual(returned_source, source) 937 938 def test_universal_newlines(self): 939 # PEP 302 says universal newlines should be used. 940 name = 'mod' 941 mock = self.SourceOnlyLoaderMock('mod.file') 942 source = "x = 42\r\ny = -13\r\n" 943 mock.source = source.encode('utf-8') 944 expect = io.IncrementalNewlineDecoder(None, True).decode(source) 945 self.assertEqual(mock.get_source(name), expect) 946 947 948class Frozen_SourceOnlyLGetSourceTests(SourceLoaderGetSourceTests, unittest.TestCase): 949 SourceOnlyLoaderMock = Frozen_SourceOnlyL 950 951class Source_SourceOnlyLGetSourceTests(SourceLoaderGetSourceTests, unittest.TestCase): 952 SourceOnlyLoaderMock = Source_SourceOnlyL 953 954 955class SourceLoaderInitModuleAttrTests: 956 957 """Tests for importlib.abc.SourceLoader.init_module_attrs().""" 958 959 def test_init_module_attrs(self): 960 # If __file__ set, __cached__ == importlib.util.cached_from_source(__file__). 961 name = 'blah' 962 path = 'blah.py' 963 loader = self.SourceOnlyLoaderMock(path) 964 module = types.ModuleType(name) 965 loader.init_module_attrs(module) 966 self.assertEqual(module.__loader__, loader) 967 self.assertEqual(module.__package__, '') 968 self.assertEqual(module.__file__, path) 969 self.assertEqual(module.__cached__, self.util.cache_from_source(path)) 970 971 def test_no_get_filename(self): 972 # No __file__, no __cached__. 973 with mock.patch.object(self.SourceOnlyLoaderMock, 'get_filename') as mocked: 974 mocked.side_effect = ImportError 975 name = 'blah' 976 loader = self.SourceOnlyLoaderMock('blah.py') 977 module = types.ModuleType(name) 978 loader.init_module_attrs(module) 979 self.assertFalse(hasattr(module, '__file__')) 980 self.assertFalse(hasattr(module, '__cached__')) 981 982 983class Frozen_SLInitModuleAttrTests(SourceLoaderInitModuleAttrTests, unittest.TestCase): 984 SourceOnlyLoaderMock = Frozen_SourceOnlyL 985 util = frozen_util 986 987 # Difficult to test under source thanks to cross-module mocking needs. 988 @mock.patch('importlib._bootstrap.cache_from_source') 989 def test_cache_from_source_NotImplementedError(self, mock_cache_from_source): 990 # If importlib.util.cache_from_source() raises NotImplementedError don't set 991 # __cached__. 992 mock_cache_from_source.side_effect = NotImplementedError 993 name = 'blah' 994 path = 'blah.py' 995 loader = self.SourceOnlyLoaderMock(path) 996 module = types.ModuleType(name) 997 loader.init_module_attrs(module) 998 self.assertEqual(module.__file__, path) 999 self.assertFalse(hasattr(module, '__cached__')) 1000 1001 1002class Source_SLInitModuleAttrTests(SourceLoaderInitModuleAttrTests, unittest.TestCase): 1003 SourceOnlyLoaderMock = Source_SourceOnlyL 1004 util = source_util 1005 1006 1007 1008if __name__ == '__main__': 1009 unittest.main() 1010