1from test.test_importlib import abc, util 2 3importlib = util.import_importlib('importlib') 4importlib_abc = util.import_importlib('importlib.abc') 5machinery = util.import_importlib('importlib.machinery') 6importlib_util = util.import_importlib('importlib.util') 7 8import errno 9import marshal 10import os 11import py_compile 12import shutil 13import stat 14import sys 15import types 16import unittest 17import warnings 18 19from test.support.import_helper import make_legacy_pyc, unload 20 21from test.test_py_compile import without_source_date_epoch 22from test.test_py_compile import SourceDateEpochTestMeta 23 24 25class SimpleTest(abc.LoaderTests): 26 27 """Should have no issue importing a source module [basic]. And if there is 28 a syntax error, it should raise a SyntaxError [syntax error]. 29 30 """ 31 32 def setUp(self): 33 self.name = 'spam' 34 self.filepath = os.path.join('ham', self.name + '.py') 35 self.loader = self.machinery.SourceFileLoader(self.name, self.filepath) 36 37 def test_load_module_API(self): 38 class Tester(self.abc.FileLoader): 39 def get_source(self, _): return 'attr = 42' 40 def is_package(self, _): return False 41 42 loader = Tester('blah', 'blah.py') 43 self.addCleanup(unload, 'blah') 44 with warnings.catch_warnings(): 45 warnings.simplefilter('ignore', DeprecationWarning) 46 module = loader.load_module() # Should not raise an exception. 47 48 def test_get_filename_API(self): 49 # If fullname is not set then assume self.path is desired. 50 class Tester(self.abc.FileLoader): 51 def get_code(self, _): pass 52 def get_source(self, _): pass 53 def is_package(self, _): pass 54 def module_repr(self, _): pass 55 56 path = 'some_path' 57 name = 'some_name' 58 loader = Tester(name, path) 59 self.assertEqual(path, loader.get_filename(name)) 60 self.assertEqual(path, loader.get_filename()) 61 self.assertEqual(path, loader.get_filename(None)) 62 with self.assertRaises(ImportError): 63 loader.get_filename(name + 'XXX') 64 65 def test_equality(self): 66 other = self.machinery.SourceFileLoader(self.name, self.filepath) 67 self.assertEqual(self.loader, other) 68 69 def test_inequality(self): 70 other = self.machinery.SourceFileLoader('_' + self.name, self.filepath) 71 self.assertNotEqual(self.loader, other) 72 73 # [basic] 74 def test_module(self): 75 with util.create_modules('_temp') as mapping: 76 loader = self.machinery.SourceFileLoader('_temp', mapping['_temp']) 77 with warnings.catch_warnings(): 78 warnings.simplefilter('ignore', DeprecationWarning) 79 module = loader.load_module('_temp') 80 self.assertIn('_temp', sys.modules) 81 check = {'__name__': '_temp', '__file__': mapping['_temp'], 82 '__package__': ''} 83 for attr, value in check.items(): 84 self.assertEqual(getattr(module, attr), value) 85 86 def test_package(self): 87 with util.create_modules('_pkg.__init__') as mapping: 88 loader = self.machinery.SourceFileLoader('_pkg', 89 mapping['_pkg.__init__']) 90 with warnings.catch_warnings(): 91 warnings.simplefilter('ignore', DeprecationWarning) 92 module = loader.load_module('_pkg') 93 self.assertIn('_pkg', sys.modules) 94 check = {'__name__': '_pkg', '__file__': mapping['_pkg.__init__'], 95 '__path__': [os.path.dirname(mapping['_pkg.__init__'])], 96 '__package__': '_pkg'} 97 for attr, value in check.items(): 98 self.assertEqual(getattr(module, attr), value) 99 100 101 def test_lacking_parent(self): 102 with util.create_modules('_pkg.__init__', '_pkg.mod')as mapping: 103 loader = self.machinery.SourceFileLoader('_pkg.mod', 104 mapping['_pkg.mod']) 105 with warnings.catch_warnings(): 106 warnings.simplefilter('ignore', DeprecationWarning) 107 module = loader.load_module('_pkg.mod') 108 self.assertIn('_pkg.mod', sys.modules) 109 check = {'__name__': '_pkg.mod', '__file__': mapping['_pkg.mod'], 110 '__package__': '_pkg'} 111 for attr, value in check.items(): 112 self.assertEqual(getattr(module, attr), value) 113 114 def fake_mtime(self, fxn): 115 """Fake mtime to always be higher than expected.""" 116 return lambda name: fxn(name) + 1 117 118 def test_module_reuse(self): 119 with util.create_modules('_temp') as mapping: 120 loader = self.machinery.SourceFileLoader('_temp', mapping['_temp']) 121 with warnings.catch_warnings(): 122 warnings.simplefilter('ignore', DeprecationWarning) 123 module = loader.load_module('_temp') 124 module_id = id(module) 125 module_dict_id = id(module.__dict__) 126 with open(mapping['_temp'], 'w', encoding='utf-8') as file: 127 file.write("testing_var = 42\n") 128 with warnings.catch_warnings(): 129 warnings.simplefilter('ignore', DeprecationWarning) 130 module = loader.load_module('_temp') 131 self.assertIn('testing_var', module.__dict__, 132 "'testing_var' not in " 133 "{0}".format(list(module.__dict__.keys()))) 134 self.assertEqual(module, sys.modules['_temp']) 135 self.assertEqual(id(module), module_id) 136 self.assertEqual(id(module.__dict__), module_dict_id) 137 138 def test_state_after_failure(self): 139 # A failed reload should leave the original module intact. 140 attributes = ('__file__', '__path__', '__package__') 141 value = '<test>' 142 name = '_temp' 143 with util.create_modules(name) as mapping: 144 orig_module = types.ModuleType(name) 145 for attr in attributes: 146 setattr(orig_module, attr, value) 147 with open(mapping[name], 'w', encoding='utf-8') as file: 148 file.write('+++ bad syntax +++') 149 loader = self.machinery.SourceFileLoader('_temp', mapping['_temp']) 150 with self.assertRaises(SyntaxError): 151 loader.exec_module(orig_module) 152 for attr in attributes: 153 self.assertEqual(getattr(orig_module, attr), value) 154 with self.assertRaises(SyntaxError): 155 with warnings.catch_warnings(): 156 warnings.simplefilter('ignore', DeprecationWarning) 157 loader.load_module(name) 158 for attr in attributes: 159 self.assertEqual(getattr(orig_module, attr), value) 160 161 # [syntax error] 162 def test_bad_syntax(self): 163 with util.create_modules('_temp') as mapping: 164 with open(mapping['_temp'], 'w', encoding='utf-8') as file: 165 file.write('=') 166 loader = self.machinery.SourceFileLoader('_temp', mapping['_temp']) 167 with self.assertRaises(SyntaxError): 168 with warnings.catch_warnings(): 169 warnings.simplefilter('ignore', DeprecationWarning) 170 loader.load_module('_temp') 171 self.assertNotIn('_temp', sys.modules) 172 173 def test_file_from_empty_string_dir(self): 174 # Loading a module found from an empty string entry on sys.path should 175 # not only work, but keep all attributes relative. 176 file_path = '_temp.py' 177 with open(file_path, 'w', encoding='utf-8') as file: 178 file.write("# test file for importlib") 179 try: 180 with util.uncache('_temp'): 181 loader = self.machinery.SourceFileLoader('_temp', file_path) 182 with warnings.catch_warnings(): 183 warnings.simplefilter('ignore', DeprecationWarning) 184 mod = loader.load_module('_temp') 185 self.assertEqual(file_path, mod.__file__) 186 self.assertEqual(self.util.cache_from_source(file_path), 187 mod.__cached__) 188 finally: 189 os.unlink(file_path) 190 pycache = os.path.dirname(self.util.cache_from_source(file_path)) 191 if os.path.exists(pycache): 192 shutil.rmtree(pycache) 193 194 @util.writes_bytecode_files 195 def test_timestamp_overflow(self): 196 # When a modification timestamp is larger than 2**32, it should be 197 # truncated rather than raise an OverflowError. 198 with util.create_modules('_temp') as mapping: 199 source = mapping['_temp'] 200 compiled = self.util.cache_from_source(source) 201 with open(source, 'w', encoding='utf-8') as f: 202 f.write("x = 5") 203 try: 204 os.utime(source, (2 ** 33 - 5, 2 ** 33 - 5)) 205 except OverflowError: 206 self.skipTest("cannot set modification time to large integer") 207 except OSError as e: 208 if e.errno != getattr(errno, 'EOVERFLOW', None): 209 raise 210 self.skipTest("cannot set modification time to large integer ({})".format(e)) 211 loader = self.machinery.SourceFileLoader('_temp', mapping['_temp']) 212 # PEP 451 213 module = types.ModuleType('_temp') 214 module.__spec__ = self.util.spec_from_loader('_temp', loader) 215 loader.exec_module(module) 216 self.assertEqual(module.x, 5) 217 self.assertTrue(os.path.exists(compiled)) 218 os.unlink(compiled) 219 # PEP 302 220 with warnings.catch_warnings(): 221 warnings.simplefilter('ignore', DeprecationWarning) 222 mod = loader.load_module('_temp') 223 # Sanity checks. 224 self.assertEqual(mod.__cached__, compiled) 225 self.assertEqual(mod.x, 5) 226 # The pyc file was created. 227 self.assertTrue(os.path.exists(compiled)) 228 229 def test_unloadable(self): 230 loader = self.machinery.SourceFileLoader('good name', {}) 231 module = types.ModuleType('bad name') 232 module.__spec__ = self.machinery.ModuleSpec('bad name', loader) 233 with self.assertRaises(ImportError): 234 loader.exec_module(module) 235 with self.assertRaises(ImportError): 236 with warnings.catch_warnings(): 237 warnings.simplefilter('ignore', DeprecationWarning) 238 loader.load_module('bad name') 239 240 @util.writes_bytecode_files 241 def test_checked_hash_based_pyc(self): 242 with util.create_modules('_temp') as mapping: 243 source = mapping['_temp'] 244 pyc = self.util.cache_from_source(source) 245 with open(source, 'wb') as fp: 246 fp.write(b'state = "old"') 247 os.utime(source, (50, 50)) 248 py_compile.compile( 249 source, 250 invalidation_mode=py_compile.PycInvalidationMode.CHECKED_HASH, 251 ) 252 loader = self.machinery.SourceFileLoader('_temp', source) 253 mod = types.ModuleType('_temp') 254 mod.__spec__ = self.util.spec_from_loader('_temp', loader) 255 loader.exec_module(mod) 256 self.assertEqual(mod.state, 'old') 257 # Write a new source with the same mtime and size as before. 258 with open(source, 'wb') as fp: 259 fp.write(b'state = "new"') 260 os.utime(source, (50, 50)) 261 loader.exec_module(mod) 262 self.assertEqual(mod.state, 'new') 263 with open(pyc, 'rb') as fp: 264 data = fp.read() 265 self.assertEqual(int.from_bytes(data[4:8], 'little'), 0b11) 266 self.assertEqual( 267 self.util.source_hash(b'state = "new"'), 268 data[8:16], 269 ) 270 271 @util.writes_bytecode_files 272 def test_overridden_checked_hash_based_pyc(self): 273 with util.create_modules('_temp') as mapping, \ 274 unittest.mock.patch('_imp.check_hash_based_pycs', 'never'): 275 source = mapping['_temp'] 276 pyc = self.util.cache_from_source(source) 277 with open(source, 'wb') as fp: 278 fp.write(b'state = "old"') 279 os.utime(source, (50, 50)) 280 py_compile.compile( 281 source, 282 invalidation_mode=py_compile.PycInvalidationMode.CHECKED_HASH, 283 ) 284 loader = self.machinery.SourceFileLoader('_temp', source) 285 mod = types.ModuleType('_temp') 286 mod.__spec__ = self.util.spec_from_loader('_temp', loader) 287 loader.exec_module(mod) 288 self.assertEqual(mod.state, 'old') 289 # Write a new source with the same mtime and size as before. 290 with open(source, 'wb') as fp: 291 fp.write(b'state = "new"') 292 os.utime(source, (50, 50)) 293 loader.exec_module(mod) 294 self.assertEqual(mod.state, 'old') 295 296 @util.writes_bytecode_files 297 def test_unchecked_hash_based_pyc(self): 298 with util.create_modules('_temp') as mapping: 299 source = mapping['_temp'] 300 pyc = self.util.cache_from_source(source) 301 with open(source, 'wb') as fp: 302 fp.write(b'state = "old"') 303 os.utime(source, (50, 50)) 304 py_compile.compile( 305 source, 306 invalidation_mode=py_compile.PycInvalidationMode.UNCHECKED_HASH, 307 ) 308 loader = self.machinery.SourceFileLoader('_temp', source) 309 mod = types.ModuleType('_temp') 310 mod.__spec__ = self.util.spec_from_loader('_temp', loader) 311 loader.exec_module(mod) 312 self.assertEqual(mod.state, 'old') 313 # Update the source file, which should be ignored. 314 with open(source, 'wb') as fp: 315 fp.write(b'state = "new"') 316 loader.exec_module(mod) 317 self.assertEqual(mod.state, 'old') 318 with open(pyc, 'rb') as fp: 319 data = fp.read() 320 self.assertEqual(int.from_bytes(data[4:8], 'little'), 0b1) 321 self.assertEqual( 322 self.util.source_hash(b'state = "old"'), 323 data[8:16], 324 ) 325 326 @util.writes_bytecode_files 327 def test_overridden_unchecked_hash_based_pyc(self): 328 with util.create_modules('_temp') as mapping, \ 329 unittest.mock.patch('_imp.check_hash_based_pycs', 'always'): 330 source = mapping['_temp'] 331 pyc = self.util.cache_from_source(source) 332 with open(source, 'wb') as fp: 333 fp.write(b'state = "old"') 334 os.utime(source, (50, 50)) 335 py_compile.compile( 336 source, 337 invalidation_mode=py_compile.PycInvalidationMode.UNCHECKED_HASH, 338 ) 339 loader = self.machinery.SourceFileLoader('_temp', source) 340 mod = types.ModuleType('_temp') 341 mod.__spec__ = self.util.spec_from_loader('_temp', loader) 342 loader.exec_module(mod) 343 self.assertEqual(mod.state, 'old') 344 # Update the source file, which should be ignored. 345 with open(source, 'wb') as fp: 346 fp.write(b'state = "new"') 347 loader.exec_module(mod) 348 self.assertEqual(mod.state, 'new') 349 with open(pyc, 'rb') as fp: 350 data = fp.read() 351 self.assertEqual(int.from_bytes(data[4:8], 'little'), 0b1) 352 self.assertEqual( 353 self.util.source_hash(b'state = "new"'), 354 data[8:16], 355 ) 356 357 358(Frozen_SimpleTest, 359 Source_SimpleTest 360 ) = util.test_both(SimpleTest, importlib=importlib, machinery=machinery, 361 abc=importlib_abc, util=importlib_util) 362 363 364class SourceDateEpochTestMeta(SourceDateEpochTestMeta, 365 type(Source_SimpleTest)): 366 pass 367 368 369class SourceDateEpoch_SimpleTest(Source_SimpleTest, 370 metaclass=SourceDateEpochTestMeta, 371 source_date_epoch=True): 372 pass 373 374 375class BadBytecodeTest: 376 377 def import_(self, file, module_name): 378 raise NotImplementedError 379 380 def manipulate_bytecode(self, 381 name, mapping, manipulator, *, 382 del_source=False, 383 invalidation_mode=py_compile.PycInvalidationMode.TIMESTAMP): 384 """Manipulate the bytecode of a module by passing it into a callable 385 that returns what to use as the new bytecode.""" 386 try: 387 del sys.modules['_temp'] 388 except KeyError: 389 pass 390 py_compile.compile(mapping[name], invalidation_mode=invalidation_mode) 391 if not del_source: 392 bytecode_path = self.util.cache_from_source(mapping[name]) 393 else: 394 os.unlink(mapping[name]) 395 bytecode_path = make_legacy_pyc(mapping[name]) 396 if manipulator: 397 with open(bytecode_path, 'rb') as file: 398 bc = file.read() 399 new_bc = manipulator(bc) 400 with open(bytecode_path, 'wb') as file: 401 if new_bc is not None: 402 file.write(new_bc) 403 return bytecode_path 404 405 def _test_empty_file(self, test, *, del_source=False): 406 with util.create_modules('_temp') as mapping: 407 bc_path = self.manipulate_bytecode('_temp', mapping, 408 lambda bc: b'', 409 del_source=del_source) 410 test('_temp', mapping, bc_path) 411 412 @util.writes_bytecode_files 413 def _test_partial_magic(self, test, *, del_source=False): 414 # When their are less than 4 bytes to a .pyc, regenerate it if 415 # possible, else raise ImportError. 416 with util.create_modules('_temp') as mapping: 417 bc_path = self.manipulate_bytecode('_temp', mapping, 418 lambda bc: bc[:3], 419 del_source=del_source) 420 test('_temp', mapping, bc_path) 421 422 def _test_magic_only(self, test, *, del_source=False): 423 with util.create_modules('_temp') as mapping: 424 bc_path = self.manipulate_bytecode('_temp', mapping, 425 lambda bc: bc[:4], 426 del_source=del_source) 427 test('_temp', mapping, bc_path) 428 429 def _test_partial_flags(self, test, *, del_source=False): 430 with util.create_modules('_temp') as mapping: 431 bc_path = self.manipulate_bytecode('_temp', mapping, 432 lambda bc: bc[:7], 433 del_source=del_source) 434 test('_temp', mapping, bc_path) 435 436 def _test_partial_hash(self, test, *, del_source=False): 437 with util.create_modules('_temp') as mapping: 438 bc_path = self.manipulate_bytecode( 439 '_temp', 440 mapping, 441 lambda bc: bc[:13], 442 del_source=del_source, 443 invalidation_mode=py_compile.PycInvalidationMode.CHECKED_HASH, 444 ) 445 test('_temp', mapping, bc_path) 446 with util.create_modules('_temp') as mapping: 447 bc_path = self.manipulate_bytecode( 448 '_temp', 449 mapping, 450 lambda bc: bc[:13], 451 del_source=del_source, 452 invalidation_mode=py_compile.PycInvalidationMode.UNCHECKED_HASH, 453 ) 454 test('_temp', mapping, bc_path) 455 456 def _test_partial_timestamp(self, test, *, del_source=False): 457 with util.create_modules('_temp') as mapping: 458 bc_path = self.manipulate_bytecode('_temp', mapping, 459 lambda bc: bc[:11], 460 del_source=del_source) 461 test('_temp', mapping, bc_path) 462 463 def _test_partial_size(self, test, *, del_source=False): 464 with util.create_modules('_temp') as mapping: 465 bc_path = self.manipulate_bytecode('_temp', mapping, 466 lambda bc: bc[:15], 467 del_source=del_source) 468 test('_temp', mapping, bc_path) 469 470 def _test_no_marshal(self, *, del_source=False): 471 with util.create_modules('_temp') as mapping: 472 bc_path = self.manipulate_bytecode('_temp', mapping, 473 lambda bc: bc[:16], 474 del_source=del_source) 475 file_path = mapping['_temp'] if not del_source else bc_path 476 with self.assertRaises(EOFError): 477 self.import_(file_path, '_temp') 478 479 def _test_non_code_marshal(self, *, del_source=False): 480 with util.create_modules('_temp') as mapping: 481 bytecode_path = self.manipulate_bytecode('_temp', mapping, 482 lambda bc: bc[:16] + marshal.dumps(b'abcd'), 483 del_source=del_source) 484 file_path = mapping['_temp'] if not del_source else bytecode_path 485 with self.assertRaises(ImportError) as cm: 486 self.import_(file_path, '_temp') 487 self.assertEqual(cm.exception.name, '_temp') 488 self.assertEqual(cm.exception.path, bytecode_path) 489 490 def _test_bad_marshal(self, *, del_source=False): 491 with util.create_modules('_temp') as mapping: 492 bytecode_path = self.manipulate_bytecode('_temp', mapping, 493 lambda bc: bc[:16] + b'<test>', 494 del_source=del_source) 495 file_path = mapping['_temp'] if not del_source else bytecode_path 496 with self.assertRaises(EOFError): 497 self.import_(file_path, '_temp') 498 499 def _test_bad_magic(self, test, *, del_source=False): 500 with util.create_modules('_temp') as mapping: 501 bc_path = self.manipulate_bytecode('_temp', mapping, 502 lambda bc: b'\x00\x00\x00\x00' + bc[4:]) 503 test('_temp', mapping, bc_path) 504 505 506class BadBytecodeTestPEP451(BadBytecodeTest): 507 508 def import_(self, file, module_name): 509 loader = self.loader(module_name, file) 510 module = types.ModuleType(module_name) 511 module.__spec__ = self.util.spec_from_loader(module_name, loader) 512 loader.exec_module(module) 513 514 515class BadBytecodeTestPEP302(BadBytecodeTest): 516 517 def import_(self, file, module_name): 518 loader = self.loader(module_name, file) 519 with warnings.catch_warnings(): 520 warnings.simplefilter('ignore', DeprecationWarning) 521 module = loader.load_module(module_name) 522 self.assertIn(module_name, sys.modules) 523 524 525class SourceLoaderBadBytecodeTest: 526 527 @classmethod 528 def setUpClass(cls): 529 cls.loader = cls.machinery.SourceFileLoader 530 531 @util.writes_bytecode_files 532 def test_empty_file(self): 533 # When a .pyc is empty, regenerate it if possible, else raise 534 # ImportError. 535 def test(name, mapping, bytecode_path): 536 self.import_(mapping[name], name) 537 with open(bytecode_path, 'rb') as file: 538 self.assertGreater(len(file.read()), 16) 539 540 self._test_empty_file(test) 541 542 def test_partial_magic(self): 543 def test(name, mapping, bytecode_path): 544 self.import_(mapping[name], name) 545 with open(bytecode_path, 'rb') as file: 546 self.assertGreater(len(file.read()), 16) 547 548 self._test_partial_magic(test) 549 550 @util.writes_bytecode_files 551 def test_magic_only(self): 552 # When there is only the magic number, regenerate the .pyc if possible, 553 # else raise EOFError. 554 def test(name, mapping, bytecode_path): 555 self.import_(mapping[name], name) 556 with open(bytecode_path, 'rb') as file: 557 self.assertGreater(len(file.read()), 16) 558 559 self._test_magic_only(test) 560 561 @util.writes_bytecode_files 562 def test_bad_magic(self): 563 # When the magic number is different, the bytecode should be 564 # regenerated. 565 def test(name, mapping, bytecode_path): 566 self.import_(mapping[name], name) 567 with open(bytecode_path, 'rb') as bytecode_file: 568 self.assertEqual(bytecode_file.read(4), 569 self.util.MAGIC_NUMBER) 570 571 self._test_bad_magic(test) 572 573 @util.writes_bytecode_files 574 def test_partial_timestamp(self): 575 # When the timestamp is partial, regenerate the .pyc, else 576 # raise EOFError. 577 def test(name, mapping, bc_path): 578 self.import_(mapping[name], name) 579 with open(bc_path, 'rb') as file: 580 self.assertGreater(len(file.read()), 16) 581 582 self._test_partial_timestamp(test) 583 584 @util.writes_bytecode_files 585 def test_partial_flags(self): 586 # When the flags is partial, regenerate the .pyc, else raise EOFError. 587 def test(name, mapping, bc_path): 588 self.import_(mapping[name], name) 589 with open(bc_path, 'rb') as file: 590 self.assertGreater(len(file.read()), 16) 591 592 self._test_partial_flags(test) 593 594 @util.writes_bytecode_files 595 def test_partial_hash(self): 596 # When the hash is partial, regenerate the .pyc, else raise EOFError. 597 def test(name, mapping, bc_path): 598 self.import_(mapping[name], name) 599 with open(bc_path, 'rb') as file: 600 self.assertGreater(len(file.read()), 16) 601 602 self._test_partial_hash(test) 603 604 @util.writes_bytecode_files 605 def test_partial_size(self): 606 # When the size is partial, regenerate the .pyc, else 607 # raise EOFError. 608 def test(name, mapping, bc_path): 609 self.import_(mapping[name], name) 610 with open(bc_path, 'rb') as file: 611 self.assertGreater(len(file.read()), 16) 612 613 self._test_partial_size(test) 614 615 @util.writes_bytecode_files 616 def test_no_marshal(self): 617 # When there is only the magic number and timestamp, raise EOFError. 618 self._test_no_marshal() 619 620 @util.writes_bytecode_files 621 def test_non_code_marshal(self): 622 self._test_non_code_marshal() 623 # XXX ImportError when sourceless 624 625 # [bad marshal] 626 @util.writes_bytecode_files 627 def test_bad_marshal(self): 628 # Bad marshal data should raise a ValueError. 629 self._test_bad_marshal() 630 631 # [bad timestamp] 632 @util.writes_bytecode_files 633 @without_source_date_epoch 634 def test_old_timestamp(self): 635 # When the timestamp is older than the source, bytecode should be 636 # regenerated. 637 zeros = b'\x00\x00\x00\x00' 638 with util.create_modules('_temp') as mapping: 639 py_compile.compile(mapping['_temp']) 640 bytecode_path = self.util.cache_from_source(mapping['_temp']) 641 with open(bytecode_path, 'r+b') as bytecode_file: 642 bytecode_file.seek(8) 643 bytecode_file.write(zeros) 644 self.import_(mapping['_temp'], '_temp') 645 source_mtime = os.path.getmtime(mapping['_temp']) 646 source_timestamp = self.importlib._pack_uint32(source_mtime) 647 with open(bytecode_path, 'rb') as bytecode_file: 648 bytecode_file.seek(8) 649 self.assertEqual(bytecode_file.read(4), source_timestamp) 650 651 # [bytecode read-only] 652 @util.writes_bytecode_files 653 def test_read_only_bytecode(self): 654 # When bytecode is read-only but should be rewritten, fail silently. 655 with util.create_modules('_temp') as mapping: 656 # Create bytecode that will need to be re-created. 657 py_compile.compile(mapping['_temp']) 658 bytecode_path = self.util.cache_from_source(mapping['_temp']) 659 with open(bytecode_path, 'r+b') as bytecode_file: 660 bytecode_file.seek(0) 661 bytecode_file.write(b'\x00\x00\x00\x00') 662 # Make the bytecode read-only. 663 os.chmod(bytecode_path, 664 stat.S_IRUSR | stat.S_IRGRP | stat.S_IROTH) 665 try: 666 # Should not raise OSError! 667 self.import_(mapping['_temp'], '_temp') 668 finally: 669 # Make writable for eventual clean-up. 670 os.chmod(bytecode_path, stat.S_IWUSR) 671 672 673class SourceLoaderBadBytecodeTestPEP451( 674 SourceLoaderBadBytecodeTest, BadBytecodeTestPEP451): 675 pass 676 677 678(Frozen_SourceBadBytecodePEP451, 679 Source_SourceBadBytecodePEP451 680 ) = util.test_both(SourceLoaderBadBytecodeTestPEP451, importlib=importlib, 681 machinery=machinery, abc=importlib_abc, 682 util=importlib_util) 683 684 685class SourceLoaderBadBytecodeTestPEP302( 686 SourceLoaderBadBytecodeTest, BadBytecodeTestPEP302): 687 pass 688 689 690(Frozen_SourceBadBytecodePEP302, 691 Source_SourceBadBytecodePEP302 692 ) = util.test_both(SourceLoaderBadBytecodeTestPEP302, importlib=importlib, 693 machinery=machinery, abc=importlib_abc, 694 util=importlib_util) 695 696 697class SourcelessLoaderBadBytecodeTest: 698 699 @classmethod 700 def setUpClass(cls): 701 cls.loader = cls.machinery.SourcelessFileLoader 702 703 def test_empty_file(self): 704 def test(name, mapping, bytecode_path): 705 with self.assertRaises(ImportError) as cm: 706 self.import_(bytecode_path, name) 707 self.assertEqual(cm.exception.name, name) 708 self.assertEqual(cm.exception.path, bytecode_path) 709 710 self._test_empty_file(test, del_source=True) 711 712 def test_partial_magic(self): 713 def test(name, mapping, bytecode_path): 714 with self.assertRaises(ImportError) as cm: 715 self.import_(bytecode_path, name) 716 self.assertEqual(cm.exception.name, name) 717 self.assertEqual(cm.exception.path, bytecode_path) 718 self._test_partial_magic(test, del_source=True) 719 720 def test_magic_only(self): 721 def test(name, mapping, bytecode_path): 722 with self.assertRaises(EOFError): 723 self.import_(bytecode_path, name) 724 725 self._test_magic_only(test, del_source=True) 726 727 def test_bad_magic(self): 728 def test(name, mapping, bytecode_path): 729 with self.assertRaises(ImportError) as cm: 730 self.import_(bytecode_path, name) 731 self.assertEqual(cm.exception.name, name) 732 self.assertEqual(cm.exception.path, bytecode_path) 733 734 self._test_bad_magic(test, del_source=True) 735 736 def test_partial_timestamp(self): 737 def test(name, mapping, bytecode_path): 738 with self.assertRaises(EOFError): 739 self.import_(bytecode_path, name) 740 741 self._test_partial_timestamp(test, del_source=True) 742 743 def test_partial_flags(self): 744 def test(name, mapping, bytecode_path): 745 with self.assertRaises(EOFError): 746 self.import_(bytecode_path, name) 747 748 self._test_partial_flags(test, del_source=True) 749 750 def test_partial_hash(self): 751 def test(name, mapping, bytecode_path): 752 with self.assertRaises(EOFError): 753 self.import_(bytecode_path, name) 754 755 self._test_partial_hash(test, del_source=True) 756 757 def test_partial_size(self): 758 def test(name, mapping, bytecode_path): 759 with self.assertRaises(EOFError): 760 self.import_(bytecode_path, name) 761 762 self._test_partial_size(test, del_source=True) 763 764 def test_no_marshal(self): 765 self._test_no_marshal(del_source=True) 766 767 def test_non_code_marshal(self): 768 self._test_non_code_marshal(del_source=True) 769 770 771class SourcelessLoaderBadBytecodeTestPEP451(SourcelessLoaderBadBytecodeTest, 772 BadBytecodeTestPEP451): 773 pass 774 775 776(Frozen_SourcelessBadBytecodePEP451, 777 Source_SourcelessBadBytecodePEP451 778 ) = util.test_both(SourcelessLoaderBadBytecodeTestPEP451, importlib=importlib, 779 machinery=machinery, abc=importlib_abc, 780 util=importlib_util) 781 782 783class SourcelessLoaderBadBytecodeTestPEP302(SourcelessLoaderBadBytecodeTest, 784 BadBytecodeTestPEP302): 785 pass 786 787 788(Frozen_SourcelessBadBytecodePEP302, 789 Source_SourcelessBadBytecodePEP302 790 ) = util.test_both(SourcelessLoaderBadBytecodeTestPEP302, importlib=importlib, 791 machinery=machinery, abc=importlib_abc, 792 util=importlib_util) 793 794 795if __name__ == '__main__': 796 unittest.main() 797