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