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