(root)/
Python-3.12.0/
Lib/
test/
test_zipimport.py
       1  import sys
       2  import os
       3  import marshal
       4  import importlib
       5  import importlib.util
       6  import struct
       7  import time
       8  import unittest
       9  import unittest.mock
      10  import warnings
      11  
      12  from test import support
      13  from test.support import import_helper
      14  from test.support import os_helper
      15  
      16  from zipfile import ZipFile, ZipInfo, ZIP_STORED, ZIP_DEFLATED
      17  
      18  import zipimport
      19  import linecache
      20  import doctest
      21  import inspect
      22  import io
      23  from traceback import extract_tb, extract_stack, print_tb
      24  try:
      25      import zlib
      26  except ImportError:
      27      zlib = None
      28  
      29  test_src = """\
      30  def get_name():
      31      return __name__
      32  def get_file():
      33      return __file__
      34  """
      35  test_co = compile(test_src, "<???>", "exec")
      36  raise_src = 'def do_raise(): raise TypeError\n'
      37  
      38  def make_pyc(co, mtime, size):
      39      data = marshal.dumps(co)
      40      pyc = (importlib.util.MAGIC_NUMBER +
      41          struct.pack("<iLL", 0,
      42                      int(mtime) & 0xFFFF_FFFF, size & 0xFFFF_FFFF) + data)
      43      return pyc
      44  
      45  def module_path_to_dotted_name(path):
      46      return path.replace(os.sep, '.')
      47  
      48  NOW = time.time()
      49  test_pyc = make_pyc(test_co, NOW, len(test_src))
      50  
      51  
      52  TESTMOD = "ziptestmodule"
      53  TESTPACK = "ziptestpackage"
      54  TESTPACK2 = "ziptestpackage2"
      55  TEMP_DIR = os.path.abspath("junk95142")
      56  TEMP_ZIP = os.path.abspath("junk95142.zip")
      57  
      58  pyc_file = importlib.util.cache_from_source(TESTMOD + '.py')
      59  pyc_ext = '.pyc'
      60  
      61  
      62  class ESC[4;38;5;81mImportHooksBaseTestCase(ESC[4;38;5;149munittestESC[4;38;5;149m.ESC[4;38;5;149mTestCase):
      63  
      64      def setUp(self):
      65          self.path = sys.path[:]
      66          self.meta_path = sys.meta_path[:]
      67          self.path_hooks = sys.path_hooks[:]
      68          sys.path_importer_cache.clear()
      69          self.modules_before = import_helper.modules_setup()
      70  
      71      def tearDown(self):
      72          sys.path[:] = self.path
      73          sys.meta_path[:] = self.meta_path
      74          sys.path_hooks[:] = self.path_hooks
      75          sys.path_importer_cache.clear()
      76          import_helper.modules_cleanup(*self.modules_before)
      77  
      78  
      79  class ESC[4;38;5;81mUncompressedZipImportTestCase(ESC[4;38;5;149mImportHooksBaseTestCase):
      80  
      81      compression = ZIP_STORED
      82  
      83      def setUp(self):
      84          # We're reusing the zip archive path, so we must clear the
      85          # cached directory info and linecache.
      86          linecache.clearcache()
      87          zipimport._zip_directory_cache.clear()
      88          ImportHooksBaseTestCase.setUp(self)
      89  
      90      def makeTree(self, files, dirName=TEMP_DIR):
      91          # Create a filesystem based set of modules/packages
      92          # defined by files under the directory dirName.
      93          self.addCleanup(os_helper.rmtree, dirName)
      94  
      95          for name, (mtime, data) in files.items():
      96              path = os.path.join(dirName, name)
      97              if path[-1] == os.sep:
      98                  if not os.path.isdir(path):
      99                      os.makedirs(path)
     100              else:
     101                  dname = os.path.dirname(path)
     102                  if not os.path.isdir(dname):
     103                      os.makedirs(dname)
     104                  with open(path, 'wb') as fp:
     105                      fp.write(data)
     106  
     107      def makeZip(self, files, zipName=TEMP_ZIP, **kw):
     108          # Create a zip archive based set of modules/packages
     109          # defined by files in the zip file zipName.  If the
     110          # key 'stuff' exists in kw it is prepended to the archive.
     111          self.addCleanup(os_helper.unlink, zipName)
     112  
     113          with ZipFile(zipName, "w") as z:
     114              for name, (mtime, data) in files.items():
     115                  zinfo = ZipInfo(name, time.localtime(mtime))
     116                  zinfo.compress_type = self.compression
     117                  z.writestr(zinfo, data)
     118              comment = kw.get("comment", None)
     119              if comment is not None:
     120                  z.comment = comment
     121  
     122          stuff = kw.get("stuff", None)
     123          if stuff is not None:
     124              # Prepend 'stuff' to the start of the zipfile
     125              with open(zipName, "rb") as f:
     126                  data = f.read()
     127              with open(zipName, "wb") as f:
     128                  f.write(stuff)
     129                  f.write(data)
     130  
     131      def doTest(self, expected_ext, files, *modules, **kw):
     132          self.makeZip(files, **kw)
     133  
     134          sys.path.insert(0, TEMP_ZIP)
     135  
     136          mod = importlib.import_module(".".join(modules))
     137  
     138          call = kw.get('call')
     139          if call is not None:
     140              call(mod)
     141  
     142          if expected_ext:
     143              file = mod.get_file()
     144              self.assertEqual(file, os.path.join(TEMP_ZIP,
     145                                   *modules) + expected_ext)
     146  
     147      def testAFakeZlib(self):
     148          #
     149          # This could cause a stack overflow before: importing zlib.py
     150          # from a compressed archive would cause zlib to be imported
     151          # which would find zlib.py in the archive, which would... etc.
     152          #
     153          # This test *must* be executed first: it must be the first one
     154          # to trigger zipimport to import zlib (zipimport caches the
     155          # zlib.decompress function object, after which the problem being
     156          # tested here wouldn't be a problem anymore...
     157          # (Hence the 'A' in the test method name: to make it the first
     158          # item in a list sorted by name, like
     159          # unittest.TestLoader.getTestCaseNames() does.)
     160          #
     161          # This test fails on platforms on which the zlib module is
     162          # statically linked, but the problem it tests for can't
     163          # occur in that case (builtin modules are always found first),
     164          # so we'll simply skip it then. Bug #765456.
     165          #
     166          if "zlib" in sys.builtin_module_names:
     167              self.skipTest('zlib is a builtin module')
     168          if "zlib" in sys.modules:
     169              del sys.modules["zlib"]
     170          files = {"zlib.py": (NOW, test_src)}
     171          try:
     172              self.doTest(".py", files, "zlib")
     173          except ImportError:
     174              if self.compression != ZIP_DEFLATED:
     175                  self.fail("expected test to not raise ImportError")
     176          else:
     177              if self.compression != ZIP_STORED:
     178                  self.fail("expected test to raise ImportError")
     179  
     180      def testPy(self):
     181          files = {TESTMOD + ".py": (NOW, test_src)}
     182          self.doTest(".py", files, TESTMOD)
     183  
     184      def testPyc(self):
     185          files = {TESTMOD + pyc_ext: (NOW, test_pyc)}
     186          self.doTest(pyc_ext, files, TESTMOD)
     187  
     188      def testBoth(self):
     189          files = {TESTMOD + ".py": (NOW, test_src),
     190                   TESTMOD + pyc_ext: (NOW, test_pyc)}
     191          self.doTest(pyc_ext, files, TESTMOD)
     192  
     193      def testUncheckedHashBasedPyc(self):
     194          source = b"state = 'old'"
     195          source_hash = importlib.util.source_hash(source)
     196          bytecode = importlib._bootstrap_external._code_to_hash_pyc(
     197              compile(source, "???", "exec"),
     198              source_hash,
     199              False, # unchecked
     200          )
     201          files = {TESTMOD + ".py": (NOW, "state = 'new'"),
     202                   TESTMOD + ".pyc": (NOW - 20, bytecode)}
     203          def check(mod):
     204              self.assertEqual(mod.state, 'old')
     205          self.doTest(None, files, TESTMOD, call=check)
     206  
     207      @unittest.mock.patch('_imp.check_hash_based_pycs', 'always')
     208      def test_checked_hash_based_change_pyc(self):
     209          source = b"state = 'old'"
     210          source_hash = importlib.util.source_hash(source)
     211          bytecode = importlib._bootstrap_external._code_to_hash_pyc(
     212              compile(source, "???", "exec"),
     213              source_hash,
     214              False,
     215          )
     216          files = {TESTMOD + ".py": (NOW, "state = 'new'"),
     217                   TESTMOD + ".pyc": (NOW - 20, bytecode)}
     218          def check(mod):
     219              self.assertEqual(mod.state, 'new')
     220          self.doTest(None, files, TESTMOD, call=check)
     221  
     222      def testEmptyPy(self):
     223          files = {TESTMOD + ".py": (NOW, "")}
     224          self.doTest(None, files, TESTMOD)
     225  
     226      def testBadMagic(self):
     227          # make pyc magic word invalid, forcing loading from .py
     228          badmagic_pyc = bytearray(test_pyc)
     229          badmagic_pyc[0] ^= 0x04  # flip an arbitrary bit
     230          files = {TESTMOD + ".py": (NOW, test_src),
     231                   TESTMOD + pyc_ext: (NOW, badmagic_pyc)}
     232          self.doTest(".py", files, TESTMOD)
     233  
     234      def testBadMagic2(self):
     235          # make pyc magic word invalid, causing an ImportError
     236          badmagic_pyc = bytearray(test_pyc)
     237          badmagic_pyc[0] ^= 0x04  # flip an arbitrary bit
     238          files = {TESTMOD + pyc_ext: (NOW, badmagic_pyc)}
     239          try:
     240              self.doTest(".py", files, TESTMOD)
     241              self.fail("This should not be reached")
     242          except zipimport.ZipImportError as exc:
     243              self.assertIsInstance(exc.__cause__, ImportError)
     244              self.assertIn("magic number", exc.__cause__.msg)
     245  
     246      def testBadMTime(self):
     247          badtime_pyc = bytearray(test_pyc)
     248          # flip the second bit -- not the first as that one isn't stored in the
     249          # .py's mtime in the zip archive.
     250          badtime_pyc[11] ^= 0x02
     251          files = {TESTMOD + ".py": (NOW, test_src),
     252                   TESTMOD + pyc_ext: (NOW, badtime_pyc)}
     253          self.doTest(".py", files, TESTMOD)
     254  
     255      def test2038MTime(self):
     256          # Make sure we can handle mtimes larger than what a 32-bit signed number
     257          # can hold.
     258          twenty_thirty_eight_pyc = make_pyc(test_co, 2**32 - 1, len(test_src))
     259          files = {TESTMOD + ".py": (NOW, test_src),
     260                   TESTMOD + pyc_ext: (NOW, twenty_thirty_eight_pyc)}
     261          self.doTest(".py", files, TESTMOD)
     262  
     263      def testPackage(self):
     264          packdir = TESTPACK + os.sep
     265          files = {packdir + "__init__" + pyc_ext: (NOW, test_pyc),
     266                   packdir + TESTMOD + pyc_ext: (NOW, test_pyc)}
     267          self.doTest(pyc_ext, files, TESTPACK, TESTMOD)
     268  
     269      def testSubPackage(self):
     270          # Test that subpackages function when loaded from zip
     271          # archives.
     272          packdir = TESTPACK + os.sep
     273          packdir2 = packdir + TESTPACK2 + os.sep
     274          files = {packdir + "__init__" + pyc_ext: (NOW, test_pyc),
     275                   packdir2 + "__init__" + pyc_ext: (NOW, test_pyc),
     276                   packdir2 + TESTMOD + pyc_ext: (NOW, test_pyc)}
     277          self.doTest(pyc_ext, files, TESTPACK, TESTPACK2, TESTMOD)
     278  
     279      def testSubNamespacePackage(self):
     280          # Test that implicit namespace subpackages function
     281          # when loaded from zip archives.
     282          packdir = TESTPACK + os.sep
     283          packdir2 = packdir + TESTPACK2 + os.sep
     284          # The first two files are just directory entries (so have no data).
     285          files = {packdir: (NOW, ""),
     286                   packdir2: (NOW, ""),
     287                   packdir2 + TESTMOD + pyc_ext: (NOW, test_pyc)}
     288          self.doTest(pyc_ext, files, TESTPACK, TESTPACK2, TESTMOD)
     289  
     290      def testMixedNamespacePackage(self):
     291          # Test implicit namespace packages spread between a
     292          # real filesystem and a zip archive.
     293          packdir = TESTPACK + os.sep
     294          packdir2 = packdir + TESTPACK2 + os.sep
     295          packdir3 = packdir2 + TESTPACK + '3' + os.sep
     296          files1 = {packdir: (NOW, ""),
     297                    packdir + TESTMOD + pyc_ext: (NOW, test_pyc),
     298                    packdir2: (NOW, ""),
     299                    packdir3: (NOW, ""),
     300                    packdir3 + TESTMOD + pyc_ext: (NOW, test_pyc),
     301                    packdir2 + TESTMOD + '3' + pyc_ext: (NOW, test_pyc),
     302                    packdir2 + TESTMOD + pyc_ext: (NOW, test_pyc)}
     303          files2 = {packdir: (NOW, ""),
     304                    packdir + TESTMOD + '2' + pyc_ext: (NOW, test_pyc),
     305                    packdir2: (NOW, ""),
     306                    packdir2 + TESTMOD + '2' + pyc_ext: (NOW, test_pyc),
     307                    packdir2 + TESTMOD + pyc_ext: (NOW, test_pyc)}
     308  
     309          zip1 = os.path.abspath("path1.zip")
     310          self.makeZip(files1, zip1)
     311  
     312          zip2 = TEMP_DIR
     313          self.makeTree(files2, zip2)
     314  
     315          # zip2 should override zip1.
     316          sys.path.insert(0, zip1)
     317          sys.path.insert(0, zip2)
     318  
     319          mod = importlib.import_module(TESTPACK)
     320  
     321          # if TESTPACK is functioning as a namespace pkg then
     322          # there should be two entries in the __path__.
     323          # First should be path2 and second path1.
     324          self.assertEqual(2, len(mod.__path__))
     325          p1, p2 = mod.__path__
     326          self.assertEqual(os.path.basename(TEMP_DIR), p1.split(os.sep)[-2])
     327          self.assertEqual("path1.zip", p2.split(os.sep)[-2])
     328  
     329          # packdir3 should import as a namespace package.
     330          # Its __path__ is an iterable of 1 element from zip1.
     331          mod = importlib.import_module(packdir3.replace(os.sep, '.')[:-1])
     332          self.assertEqual(1, len(mod.__path__))
     333          mpath = list(mod.__path__)[0].split('path1.zip' + os.sep)[1]
     334          self.assertEqual(packdir3[:-1], mpath)
     335  
     336          # TESTPACK/TESTMOD only exists in path1.
     337          mod = importlib.import_module('.'.join((TESTPACK, TESTMOD)))
     338          self.assertEqual("path1.zip", mod.__file__.split(os.sep)[-3])
     339  
     340          # And TESTPACK/(TESTMOD + '2') only exists in path2.
     341          mod = importlib.import_module('.'.join((TESTPACK, TESTMOD + '2')))
     342          self.assertEqual(os.path.basename(TEMP_DIR),
     343                           mod.__file__.split(os.sep)[-3])
     344  
     345          # One level deeper...
     346          subpkg = '.'.join((TESTPACK, TESTPACK2))
     347          mod = importlib.import_module(subpkg)
     348          self.assertEqual(2, len(mod.__path__))
     349          p1, p2 = mod.__path__
     350          self.assertEqual(os.path.basename(TEMP_DIR), p1.split(os.sep)[-3])
     351          self.assertEqual("path1.zip", p2.split(os.sep)[-3])
     352  
     353          # subpkg.TESTMOD exists in both zips should load from zip2.
     354          mod = importlib.import_module('.'.join((subpkg, TESTMOD)))
     355          self.assertEqual(os.path.basename(TEMP_DIR),
     356                           mod.__file__.split(os.sep)[-4])
     357  
     358          # subpkg.TESTMOD + '2' only exists in zip2.
     359          mod = importlib.import_module('.'.join((subpkg, TESTMOD + '2')))
     360          self.assertEqual(os.path.basename(TEMP_DIR),
     361                           mod.__file__.split(os.sep)[-4])
     362  
     363          # Finally subpkg.TESTMOD + '3' only exists in zip1.
     364          mod = importlib.import_module('.'.join((subpkg, TESTMOD + '3')))
     365          self.assertEqual('path1.zip', mod.__file__.split(os.sep)[-4])
     366  
     367      def testNamespacePackage(self):
     368          # Test implicit namespace packages spread between multiple zip
     369          # archives.
     370          packdir = TESTPACK + os.sep
     371          packdir2 = packdir + TESTPACK2 + os.sep
     372          packdir3 = packdir2 + TESTPACK + '3' + os.sep
     373          files1 = {packdir: (NOW, ""),
     374                    packdir + TESTMOD + pyc_ext: (NOW, test_pyc),
     375                    packdir2: (NOW, ""),
     376                    packdir3: (NOW, ""),
     377                    packdir3 + TESTMOD + pyc_ext: (NOW, test_pyc),
     378                    packdir2 + TESTMOD + '3' + pyc_ext: (NOW, test_pyc),
     379                    packdir2 + TESTMOD + pyc_ext: (NOW, test_pyc)}
     380          zip1 = os.path.abspath("path1.zip")
     381          self.makeZip(files1, zip1)
     382  
     383          files2 = {packdir: (NOW, ""),
     384                    packdir + TESTMOD + '2' + pyc_ext: (NOW, test_pyc),
     385                    packdir2: (NOW, ""),
     386                    packdir2 + TESTMOD + '2' + pyc_ext: (NOW, test_pyc),
     387                    packdir2 + TESTMOD + pyc_ext: (NOW, test_pyc)}
     388          zip2 = os.path.abspath("path2.zip")
     389          self.makeZip(files2, zip2)
     390  
     391          # zip2 should override zip1.
     392          sys.path.insert(0, zip1)
     393          sys.path.insert(0, zip2)
     394  
     395          mod = importlib.import_module(TESTPACK)
     396  
     397          # if TESTPACK is functioning as a namespace pkg then
     398          # there should be two entries in the __path__.
     399          # First should be path2 and second path1.
     400          self.assertEqual(2, len(mod.__path__))
     401          p1, p2 = mod.__path__
     402          self.assertEqual("path2.zip", p1.split(os.sep)[-2])
     403          self.assertEqual("path1.zip", p2.split(os.sep)[-2])
     404  
     405          # packdir3 should import as a namespace package.
     406          # Tts __path__ is an iterable of 1 element from zip1.
     407          mod = importlib.import_module(packdir3.replace(os.sep, '.')[:-1])
     408          self.assertEqual(1, len(mod.__path__))
     409          mpath = list(mod.__path__)[0].split('path1.zip' + os.sep)[1]
     410          self.assertEqual(packdir3[:-1], mpath)
     411  
     412          # TESTPACK/TESTMOD only exists in path1.
     413          mod = importlib.import_module('.'.join((TESTPACK, TESTMOD)))
     414          self.assertEqual("path1.zip", mod.__file__.split(os.sep)[-3])
     415  
     416          # And TESTPACK/(TESTMOD + '2') only exists in path2.
     417          mod = importlib.import_module('.'.join((TESTPACK, TESTMOD + '2')))
     418          self.assertEqual("path2.zip", mod.__file__.split(os.sep)[-3])
     419  
     420          # One level deeper...
     421          subpkg = '.'.join((TESTPACK, TESTPACK2))
     422          mod = importlib.import_module(subpkg)
     423          self.assertEqual(2, len(mod.__path__))
     424          p1, p2 = mod.__path__
     425          self.assertEqual("path2.zip", p1.split(os.sep)[-3])
     426          self.assertEqual("path1.zip", p2.split(os.sep)[-3])
     427  
     428          # subpkg.TESTMOD exists in both zips should load from zip2.
     429          mod = importlib.import_module('.'.join((subpkg, TESTMOD)))
     430          self.assertEqual('path2.zip', mod.__file__.split(os.sep)[-4])
     431  
     432          # subpkg.TESTMOD + '2' only exists in zip2.
     433          mod = importlib.import_module('.'.join((subpkg, TESTMOD + '2')))
     434          self.assertEqual('path2.zip', mod.__file__.split(os.sep)[-4])
     435  
     436          # Finally subpkg.TESTMOD + '3' only exists in zip1.
     437          mod = importlib.import_module('.'.join((subpkg, TESTMOD + '3')))
     438          self.assertEqual('path1.zip', mod.__file__.split(os.sep)[-4])
     439  
     440      def testZipImporterMethods(self):
     441          packdir = TESTPACK + os.sep
     442          packdir2 = packdir + TESTPACK2 + os.sep
     443          files = {packdir + "__init__" + pyc_ext: (NOW, test_pyc),
     444                   packdir2 + "__init__" + pyc_ext: (NOW, test_pyc),
     445                   packdir2 + TESTMOD + pyc_ext: (NOW, test_pyc),
     446                   "spam" + pyc_ext: (NOW, test_pyc)}
     447  
     448          self.addCleanup(os_helper.unlink, TEMP_ZIP)
     449          with ZipFile(TEMP_ZIP, "w") as z:
     450              for name, (mtime, data) in files.items():
     451                  zinfo = ZipInfo(name, time.localtime(mtime))
     452                  zinfo.compress_type = self.compression
     453                  zinfo.comment = b"spam"
     454                  z.writestr(zinfo, data)
     455  
     456          zi = zipimport.zipimporter(TEMP_ZIP)
     457          self.assertEqual(zi.archive, TEMP_ZIP)
     458          self.assertTrue(zi.is_package(TESTPACK))
     459  
     460          # PEP 302
     461          with warnings.catch_warnings():
     462              warnings.simplefilter("ignore", DeprecationWarning)
     463  
     464              mod = zi.load_module(TESTPACK)
     465              self.assertEqual(zi.get_filename(TESTPACK), mod.__file__)
     466  
     467          # PEP 451
     468          spec = zi.find_spec('spam')
     469          self.assertIsNotNone(spec)
     470          self.assertIsInstance(spec.loader, zipimport.zipimporter)
     471          self.assertFalse(spec.loader.is_package('spam'))
     472          exec_mod = importlib.util.module_from_spec(spec)
     473          spec.loader.exec_module(exec_mod)
     474          self.assertEqual(spec.loader.get_filename('spam'), exec_mod.__file__)
     475  
     476          spec = zi.find_spec(TESTPACK)
     477          mod = importlib.util.module_from_spec(spec)
     478          spec.loader.exec_module(mod)
     479          self.assertEqual(zi.get_filename(TESTPACK), mod.__file__)
     480  
     481          existing_pack_path = importlib.import_module(TESTPACK).__path__[0]
     482          expected_path_path = os.path.join(TEMP_ZIP, TESTPACK)
     483          self.assertEqual(existing_pack_path, expected_path_path)
     484  
     485          self.assertFalse(zi.is_package(packdir + '__init__'))
     486          self.assertTrue(zi.is_package(packdir + TESTPACK2))
     487          self.assertFalse(zi.is_package(packdir2 + TESTMOD))
     488  
     489          mod_path = packdir2 + TESTMOD
     490          mod_name = module_path_to_dotted_name(mod_path)
     491          mod = importlib.import_module(mod_name)
     492          self.assertTrue(mod_name in sys.modules)
     493          self.assertIsNone(zi.get_source(TESTPACK))
     494          self.assertIsNone(zi.get_source(mod_path))
     495          self.assertEqual(zi.get_filename(mod_path), mod.__file__)
     496          # To pass in the module name instead of the path, we must use the
     497          # right importer
     498          loader = mod.__spec__.loader
     499          self.assertIsNone(loader.get_source(mod_name))
     500          self.assertEqual(loader.get_filename(mod_name), mod.__file__)
     501  
     502          # test prefix and archivepath members
     503          zi2 = zipimport.zipimporter(TEMP_ZIP + os.sep + TESTPACK)
     504          self.assertEqual(zi2.archive, TEMP_ZIP)
     505          self.assertEqual(zi2.prefix, TESTPACK + os.sep)
     506  
     507      def testInvalidateCaches(self):
     508          packdir = TESTPACK + os.sep
     509          packdir2 = packdir + TESTPACK2 + os.sep
     510          files = {packdir + "__init__" + pyc_ext: (NOW, test_pyc),
     511                   packdir2 + "__init__" + pyc_ext: (NOW, test_pyc),
     512                   packdir2 + TESTMOD + pyc_ext: (NOW, test_pyc),
     513                   "spam" + pyc_ext: (NOW, test_pyc)}
     514          self.addCleanup(os_helper.unlink, TEMP_ZIP)
     515          with ZipFile(TEMP_ZIP, "w") as z:
     516              for name, (mtime, data) in files.items():
     517                  zinfo = ZipInfo(name, time.localtime(mtime))
     518                  zinfo.compress_type = self.compression
     519                  zinfo.comment = b"spam"
     520                  z.writestr(zinfo, data)
     521  
     522          zi = zipimport.zipimporter(TEMP_ZIP)
     523          self.assertEqual(zi._files.keys(), files.keys())
     524          # Check that the file information remains accurate after reloading
     525          zi.invalidate_caches()
     526          self.assertEqual(zi._files.keys(), files.keys())
     527          # Add a new file to the ZIP archive
     528          newfile = {"spam2" + pyc_ext: (NOW, test_pyc)}
     529          files.update(newfile)
     530          with ZipFile(TEMP_ZIP, "a") as z:
     531              for name, (mtime, data) in newfile.items():
     532                  zinfo = ZipInfo(name, time.localtime(mtime))
     533                  zinfo.compress_type = self.compression
     534                  zinfo.comment = b"spam"
     535                  z.writestr(zinfo, data)
     536          # Check that we can detect the new file after invalidating the cache
     537          zi.invalidate_caches()
     538          self.assertEqual(zi._files.keys(), files.keys())
     539          spec = zi.find_spec('spam2')
     540          self.assertIsNotNone(spec)
     541          self.assertIsInstance(spec.loader, zipimport.zipimporter)
     542          # Check that the cached data is removed if the file is deleted
     543          os.remove(TEMP_ZIP)
     544          zi.invalidate_caches()
     545          self.assertFalse(zi._files)
     546          self.assertIsNone(zipimport._zip_directory_cache.get(zi.archive))
     547          self.assertIsNone(zi.find_spec("name_does_not_matter"))
     548  
     549      def testZipImporterMethodsInSubDirectory(self):
     550          packdir = TESTPACK + os.sep
     551          packdir2 = packdir + TESTPACK2 + os.sep
     552          files = {packdir2 + "__init__" + pyc_ext: (NOW, test_pyc),
     553                   packdir2 + TESTMOD + pyc_ext: (NOW, test_pyc)}
     554  
     555          self.addCleanup(os_helper.unlink, TEMP_ZIP)
     556          with ZipFile(TEMP_ZIP, "w") as z:
     557              for name, (mtime, data) in files.items():
     558                  zinfo = ZipInfo(name, time.localtime(mtime))
     559                  zinfo.compress_type = self.compression
     560                  zinfo.comment = b"eggs"
     561                  z.writestr(zinfo, data)
     562  
     563          zi = zipimport.zipimporter(TEMP_ZIP + os.sep + packdir)
     564          self.assertEqual(zi.archive, TEMP_ZIP)
     565          self.assertEqual(zi.prefix, packdir)
     566          self.assertTrue(zi.is_package(TESTPACK2))
     567          # PEP 302
     568          with warnings.catch_warnings():
     569              warnings.simplefilter("ignore", DeprecationWarning)
     570              mod = zi.load_module(TESTPACK2)
     571              self.assertEqual(zi.get_filename(TESTPACK2), mod.__file__)
     572          # PEP 451
     573          spec = zi.find_spec(TESTPACK2)
     574          mod = importlib.util.module_from_spec(spec)
     575          spec.loader.exec_module(mod)
     576          self.assertEqual(spec.loader.get_filename(TESTPACK2), mod.__file__)
     577  
     578          self.assertFalse(zi.is_package(TESTPACK2 + os.sep + '__init__'))
     579          self.assertFalse(zi.is_package(TESTPACK2 + os.sep + TESTMOD))
     580  
     581          pkg_path = TEMP_ZIP + os.sep + packdir + TESTPACK2
     582          zi2 = zipimport.zipimporter(pkg_path)
     583  
     584          # PEP 451
     585          spec = zi2.find_spec(TESTMOD)
     586          self.assertIsNotNone(spec)
     587          self.assertIsInstance(spec.loader, zipimport.zipimporter)
     588          self.assertFalse(spec.loader.is_package(TESTMOD))
     589          load_mod = importlib.util.module_from_spec(spec)
     590          spec.loader.exec_module(load_mod)
     591          self.assertEqual(
     592              spec.loader.get_filename(TESTMOD), load_mod.__file__)
     593  
     594          mod_path = TESTPACK2 + os.sep + TESTMOD
     595          mod_name = module_path_to_dotted_name(mod_path)
     596          mod = importlib.import_module(mod_name)
     597          self.assertTrue(mod_name in sys.modules)
     598          self.assertIsNone(zi.get_source(TESTPACK2))
     599          self.assertIsNone(zi.get_source(mod_path))
     600          self.assertEqual(zi.get_filename(mod_path), mod.__file__)
     601          # To pass in the module name instead of the path, we must use the
     602          # right importer.
     603          loader = mod.__loader__
     604          self.assertIsNone(loader.get_source(mod_name))
     605          self.assertEqual(loader.get_filename(mod_name), mod.__file__)
     606  
     607      def testGetData(self):
     608          self.addCleanup(os_helper.unlink, TEMP_ZIP)
     609          with ZipFile(TEMP_ZIP, "w") as z:
     610              z.compression = self.compression
     611              name = "testdata.dat"
     612              data = bytes(x for x in range(256))
     613              z.writestr(name, data)
     614  
     615          zi = zipimport.zipimporter(TEMP_ZIP)
     616          self.assertEqual(data, zi.get_data(name))
     617          self.assertIn('zipimporter object', repr(zi))
     618  
     619      def testImporterAttr(self):
     620          src = """if 1:  # indent hack
     621          def get_file():
     622              return __file__
     623          if __loader__.get_data("some.data") != b"some data":
     624              raise AssertionError("bad data")\n"""
     625          pyc = make_pyc(compile(src, "<???>", "exec"), NOW, len(src))
     626          files = {TESTMOD + pyc_ext: (NOW, pyc),
     627                   "some.data": (NOW, "some data")}
     628          self.doTest(pyc_ext, files, TESTMOD)
     629  
     630      def testDefaultOptimizationLevel(self):
     631          # zipimport should use the default optimization level (#28131)
     632          src = """if 1:  # indent hack
     633          def test(val):
     634              assert(val)
     635              return val\n"""
     636          files = {TESTMOD + '.py': (NOW, src)}
     637          self.makeZip(files)
     638          sys.path.insert(0, TEMP_ZIP)
     639          mod = importlib.import_module(TESTMOD)
     640          self.assertEqual(mod.test(1), 1)
     641          if __debug__:
     642              self.assertRaises(AssertionError, mod.test, False)
     643          else:
     644              self.assertEqual(mod.test(0), 0)
     645  
     646      def testImport_WithStuff(self):
     647          # try importing from a zipfile which contains additional
     648          # stuff at the beginning of the file
     649          files = {TESTMOD + ".py": (NOW, test_src)}
     650          self.doTest(".py", files, TESTMOD,
     651                      stuff=b"Some Stuff"*31)
     652  
     653      def assertModuleSource(self, module):
     654          self.assertEqual(inspect.getsource(module), test_src)
     655  
     656      def testGetSource(self):
     657          files = {TESTMOD + ".py": (NOW, test_src)}
     658          self.doTest(".py", files, TESTMOD, call=self.assertModuleSource)
     659  
     660      def testGetCompiledSource(self):
     661          pyc = make_pyc(compile(test_src, "<???>", "exec"), NOW, len(test_src))
     662          files = {TESTMOD + ".py": (NOW, test_src),
     663                   TESTMOD + pyc_ext: (NOW, pyc)}
     664          self.doTest(pyc_ext, files, TESTMOD, call=self.assertModuleSource)
     665  
     666      def runDoctest(self, callback):
     667          files = {TESTMOD + ".py": (NOW, test_src),
     668                   "xyz.txt": (NOW, ">>> log.append(True)\n")}
     669          self.doTest(".py", files, TESTMOD, call=callback)
     670  
     671      def doDoctestFile(self, module):
     672          log = []
     673          old_master, doctest.master = doctest.master, None
     674          try:
     675              doctest.testfile(
     676                  'xyz.txt', package=module, module_relative=True,
     677                  globs=locals()
     678              )
     679          finally:
     680              doctest.master = old_master
     681          self.assertEqual(log,[True])
     682  
     683      def testDoctestFile(self):
     684          self.runDoctest(self.doDoctestFile)
     685  
     686      def doDoctestSuite(self, module):
     687          log = []
     688          doctest.DocFileTest(
     689              'xyz.txt', package=module, module_relative=True,
     690              globs=locals()
     691          ).run()
     692          self.assertEqual(log,[True])
     693  
     694      def testDoctestSuite(self):
     695          self.runDoctest(self.doDoctestSuite)
     696  
     697      def doTraceback(self, module):
     698          try:
     699              module.do_raise()
     700          except Exception as e:
     701              tb = e.__traceback__.tb_next
     702  
     703              f,lno,n,line = extract_tb(tb, 1)[0]
     704              self.assertEqual(line, raise_src.strip())
     705  
     706              f,lno,n,line = extract_stack(tb.tb_frame, 1)[0]
     707              self.assertEqual(line, raise_src.strip())
     708  
     709              s = io.StringIO()
     710              print_tb(tb, 1, s)
     711              self.assertTrue(s.getvalue().endswith(
     712                  '    def do_raise(): raise TypeError\n'
     713                  '' if support.has_no_debug_ranges() else
     714                  '                    ^^^^^^^^^^^^^^^\n'
     715              ))
     716          else:
     717              raise AssertionError("This ought to be impossible")
     718  
     719      def testTraceback(self):
     720          files = {TESTMOD + ".py": (NOW, raise_src)}
     721          self.doTest(None, files, TESTMOD, call=self.doTraceback)
     722  
     723      @unittest.skipIf(os_helper.TESTFN_UNENCODABLE is None,
     724                       "need an unencodable filename")
     725      def testUnencodable(self):
     726          filename = os_helper.TESTFN_UNENCODABLE + ".zip"
     727          self.addCleanup(os_helper.unlink, filename)
     728          with ZipFile(filename, "w") as z:
     729              zinfo = ZipInfo(TESTMOD + ".py", time.localtime(NOW))
     730              zinfo.compress_type = self.compression
     731              z.writestr(zinfo, test_src)
     732          spec = zipimport.zipimporter(filename).find_spec(TESTMOD)
     733          mod = importlib.util.module_from_spec(spec)
     734          spec.loader.exec_module(mod)
     735  
     736      def testBytesPath(self):
     737          filename = os_helper.TESTFN + ".zip"
     738          self.addCleanup(os_helper.unlink, filename)
     739          with ZipFile(filename, "w") as z:
     740              zinfo = ZipInfo(TESTMOD + ".py", time.localtime(NOW))
     741              zinfo.compress_type = self.compression
     742              z.writestr(zinfo, test_src)
     743  
     744          zipimport.zipimporter(filename)
     745          with self.assertRaises(TypeError):
     746              zipimport.zipimporter(os.fsencode(filename))
     747          with self.assertRaises(TypeError):
     748              zipimport.zipimporter(bytearray(os.fsencode(filename)))
     749          with self.assertRaises(TypeError):
     750              zipimport.zipimporter(memoryview(os.fsencode(filename)))
     751  
     752      def testComment(self):
     753          files = {TESTMOD + ".py": (NOW, test_src)}
     754          self.doTest(".py", files, TESTMOD, comment=b"comment")
     755  
     756      def testBeginningCruftAndComment(self):
     757          files = {TESTMOD + ".py": (NOW, test_src)}
     758          self.doTest(".py", files, TESTMOD, stuff=b"cruft" * 64, comment=b"hi")
     759  
     760      def testLargestPossibleComment(self):
     761          files = {TESTMOD + ".py": (NOW, test_src)}
     762          self.doTest(".py", files, TESTMOD, comment=b"c" * ((1 << 16) - 1))
     763  
     764  
     765  @support.requires_zlib()
     766  class ESC[4;38;5;81mCompressedZipImportTestCase(ESC[4;38;5;149mUncompressedZipImportTestCase):
     767      compression = ZIP_DEFLATED
     768  
     769  
     770  class ESC[4;38;5;81mBadFileZipImportTestCase(ESC[4;38;5;149munittestESC[4;38;5;149m.ESC[4;38;5;149mTestCase):
     771      def assertZipFailure(self, filename):
     772          self.assertRaises(zipimport.ZipImportError,
     773                            zipimport.zipimporter, filename)
     774  
     775      def testNoFile(self):
     776          self.assertZipFailure('AdfjdkFJKDFJjdklfjs')
     777  
     778      def testEmptyFilename(self):
     779          self.assertZipFailure('')
     780  
     781      def testBadArgs(self):
     782          self.assertRaises(TypeError, zipimport.zipimporter, None)
     783          self.assertRaises(TypeError, zipimport.zipimporter, TESTMOD, kwd=None)
     784          self.assertRaises(TypeError, zipimport.zipimporter,
     785                            list(os.fsencode(TESTMOD)))
     786  
     787      def testFilenameTooLong(self):
     788          self.assertZipFailure('A' * 33000)
     789  
     790      def testEmptyFile(self):
     791          os_helper.unlink(TESTMOD)
     792          os_helper.create_empty_file(TESTMOD)
     793          self.assertZipFailure(TESTMOD)
     794  
     795      @unittest.skipIf(support.is_wasi, "mode 000 not supported.")
     796      def testFileUnreadable(self):
     797          os_helper.unlink(TESTMOD)
     798          fd = os.open(TESTMOD, os.O_CREAT, 000)
     799          try:
     800              os.close(fd)
     801  
     802              with self.assertRaises(zipimport.ZipImportError) as cm:
     803                  zipimport.zipimporter(TESTMOD)
     804          finally:
     805              # If we leave "the read-only bit" set on Windows, nothing can
     806              # delete TESTMOD, and later tests suffer bogus failures.
     807              os.chmod(TESTMOD, 0o666)
     808              os_helper.unlink(TESTMOD)
     809  
     810      def testNotZipFile(self):
     811          os_helper.unlink(TESTMOD)
     812          fp = open(TESTMOD, 'w+')
     813          fp.write('a' * 22)
     814          fp.close()
     815          self.assertZipFailure(TESTMOD)
     816  
     817      # XXX: disabled until this works on Big-endian machines
     818      def _testBogusZipFile(self):
     819          os_helper.unlink(TESTMOD)
     820          fp = open(TESTMOD, 'w+')
     821          fp.write(struct.pack('=I', 0x06054B50))
     822          fp.write('a' * 18)
     823          fp.close()
     824          z = zipimport.zipimporter(TESTMOD)
     825  
     826          try:
     827              with warnings.catch_warnings():
     828                  warnings.simplefilter("ignore", DeprecationWarning)
     829                  self.assertRaises(TypeError, z.load_module, None)
     830              self.assertRaises(TypeError, z.find_module, None)
     831              self.assertRaises(TypeError, z.find_spec, None)
     832              self.assertRaises(TypeError, z.exec_module, None)
     833              self.assertRaises(TypeError, z.is_package, None)
     834              self.assertRaises(TypeError, z.get_code, None)
     835              self.assertRaises(TypeError, z.get_data, None)
     836              self.assertRaises(TypeError, z.get_source, None)
     837  
     838              error = zipimport.ZipImportError
     839              self.assertIsNone(z.find_spec('abc'))
     840  
     841              with warnings.catch_warnings():
     842                  warnings.simplefilter("ignore", DeprecationWarning)
     843                  self.assertRaises(error, z.load_module, 'abc')
     844              self.assertRaises(error, z.get_code, 'abc')
     845              self.assertRaises(OSError, z.get_data, 'abc')
     846              self.assertRaises(error, z.get_source, 'abc')
     847              self.assertRaises(error, z.is_package, 'abc')
     848          finally:
     849              zipimport._zip_directory_cache.clear()
     850  
     851  
     852  def tearDownModule():
     853      os_helper.unlink(TESTMOD)
     854  
     855  
     856  if __name__ == "__main__":
     857      unittest.main()