(root)/
Python-3.11.7/
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              find_mod = zi.find_module('spam')
     464              self.assertIsNotNone(find_mod)
     465              self.assertIsInstance(find_mod, zipimport.zipimporter)
     466              self.assertFalse(find_mod.is_package('spam'))
     467              load_mod = find_mod.load_module('spam')
     468              self.assertEqual(find_mod.get_filename('spam'), load_mod.__file__)
     469  
     470              mod = zi.load_module(TESTPACK)
     471              self.assertEqual(zi.get_filename(TESTPACK), mod.__file__)
     472  
     473          # PEP 451
     474          spec = zi.find_spec('spam')
     475          self.assertIsNotNone(spec)
     476          self.assertIsInstance(spec.loader, zipimport.zipimporter)
     477          self.assertFalse(spec.loader.is_package('spam'))
     478          exec_mod = importlib.util.module_from_spec(spec)
     479          spec.loader.exec_module(exec_mod)
     480          self.assertEqual(spec.loader.get_filename('spam'), exec_mod.__file__)
     481  
     482          spec = zi.find_spec(TESTPACK)
     483          mod = importlib.util.module_from_spec(spec)
     484          spec.loader.exec_module(mod)
     485          self.assertEqual(zi.get_filename(TESTPACK), mod.__file__)
     486  
     487          existing_pack_path = importlib.import_module(TESTPACK).__path__[0]
     488          expected_path_path = os.path.join(TEMP_ZIP, TESTPACK)
     489          self.assertEqual(existing_pack_path, expected_path_path)
     490  
     491          self.assertFalse(zi.is_package(packdir + '__init__'))
     492          self.assertTrue(zi.is_package(packdir + TESTPACK2))
     493          self.assertFalse(zi.is_package(packdir2 + TESTMOD))
     494  
     495          mod_path = packdir2 + TESTMOD
     496          mod_name = module_path_to_dotted_name(mod_path)
     497          mod = importlib.import_module(mod_name)
     498          self.assertTrue(mod_name in sys.modules)
     499          self.assertIsNone(zi.get_source(TESTPACK))
     500          self.assertIsNone(zi.get_source(mod_path))
     501          self.assertEqual(zi.get_filename(mod_path), mod.__file__)
     502          # To pass in the module name instead of the path, we must use the
     503          # right importer
     504          loader = mod.__spec__.loader
     505          self.assertIsNone(loader.get_source(mod_name))
     506          self.assertEqual(loader.get_filename(mod_name), mod.__file__)
     507  
     508          # test prefix and archivepath members
     509          zi2 = zipimport.zipimporter(TEMP_ZIP + os.sep + TESTPACK)
     510          self.assertEqual(zi2.archive, TEMP_ZIP)
     511          self.assertEqual(zi2.prefix, TESTPACK + os.sep)
     512  
     513      def testInvalidateCaches(self):
     514          packdir = TESTPACK + os.sep
     515          packdir2 = packdir + TESTPACK2 + os.sep
     516          files = {packdir + "__init__" + pyc_ext: (NOW, test_pyc),
     517                   packdir2 + "__init__" + pyc_ext: (NOW, test_pyc),
     518                   packdir2 + TESTMOD + pyc_ext: (NOW, test_pyc),
     519                   "spam" + pyc_ext: (NOW, test_pyc)}
     520          self.addCleanup(os_helper.unlink, TEMP_ZIP)
     521          with ZipFile(TEMP_ZIP, "w") as z:
     522              for name, (mtime, data) in files.items():
     523                  zinfo = ZipInfo(name, time.localtime(mtime))
     524                  zinfo.compress_type = self.compression
     525                  zinfo.comment = b"spam"
     526                  z.writestr(zinfo, data)
     527  
     528          zi = zipimport.zipimporter(TEMP_ZIP)
     529          self.assertEqual(zi._files.keys(), files.keys())
     530          # Check that the file information remains accurate after reloading
     531          zi.invalidate_caches()
     532          self.assertEqual(zi._files.keys(), files.keys())
     533          # Add a new file to the ZIP archive
     534          newfile = {"spam2" + pyc_ext: (NOW, test_pyc)}
     535          files.update(newfile)
     536          with ZipFile(TEMP_ZIP, "a") as z:
     537              for name, (mtime, data) in newfile.items():
     538                  zinfo = ZipInfo(name, time.localtime(mtime))
     539                  zinfo.compress_type = self.compression
     540                  zinfo.comment = b"spam"
     541                  z.writestr(zinfo, data)
     542          # Check that we can detect the new file after invalidating the cache
     543          zi.invalidate_caches()
     544          self.assertEqual(zi._files.keys(), files.keys())
     545          spec = zi.find_spec('spam2')
     546          self.assertIsNotNone(spec)
     547          self.assertIsInstance(spec.loader, zipimport.zipimporter)
     548          # Check that the cached data is removed if the file is deleted
     549          os.remove(TEMP_ZIP)
     550          zi.invalidate_caches()
     551          self.assertFalse(zi._files)
     552          self.assertIsNone(zipimport._zip_directory_cache.get(zi.archive))
     553          self.assertIsNone(zi.find_spec("name_does_not_matter"))
     554  
     555      def testZipImporterMethodsInSubDirectory(self):
     556          packdir = TESTPACK + os.sep
     557          packdir2 = packdir + TESTPACK2 + os.sep
     558          files = {packdir2 + "__init__" + pyc_ext: (NOW, test_pyc),
     559                   packdir2 + TESTMOD + pyc_ext: (NOW, test_pyc)}
     560  
     561          self.addCleanup(os_helper.unlink, TEMP_ZIP)
     562          with ZipFile(TEMP_ZIP, "w") as z:
     563              for name, (mtime, data) in files.items():
     564                  zinfo = ZipInfo(name, time.localtime(mtime))
     565                  zinfo.compress_type = self.compression
     566                  zinfo.comment = b"eggs"
     567                  z.writestr(zinfo, data)
     568  
     569          zi = zipimport.zipimporter(TEMP_ZIP + os.sep + packdir)
     570          self.assertEqual(zi.archive, TEMP_ZIP)
     571          self.assertEqual(zi.prefix, packdir)
     572          self.assertTrue(zi.is_package(TESTPACK2))
     573          # PEP 302
     574          with warnings.catch_warnings():
     575              warnings.simplefilter("ignore", DeprecationWarning)
     576              mod = zi.load_module(TESTPACK2)
     577              self.assertEqual(zi.get_filename(TESTPACK2), mod.__file__)
     578          # PEP 451
     579          spec = zi.find_spec(TESTPACK2)
     580          mod = importlib.util.module_from_spec(spec)
     581          spec.loader.exec_module(mod)
     582          self.assertEqual(spec.loader.get_filename(TESTPACK2), mod.__file__)
     583  
     584          self.assertFalse(zi.is_package(TESTPACK2 + os.sep + '__init__'))
     585          self.assertFalse(zi.is_package(TESTPACK2 + os.sep + TESTMOD))
     586  
     587          pkg_path = TEMP_ZIP + os.sep + packdir + TESTPACK2
     588          zi2 = zipimport.zipimporter(pkg_path)
     589          # PEP 302
     590          with warnings.catch_warnings():
     591              warnings.simplefilter("ignore", DeprecationWarning)
     592              find_mod_dotted = zi2.find_module(TESTMOD)
     593              self.assertIsNotNone(find_mod_dotted)
     594              self.assertIsInstance(find_mod_dotted, zipimport.zipimporter)
     595              self.assertFalse(zi2.is_package(TESTMOD))
     596              load_mod = find_mod_dotted.load_module(TESTMOD)
     597              self.assertEqual(
     598                  find_mod_dotted.get_filename(TESTMOD), load_mod.__file__)
     599  
     600          # PEP 451
     601          spec = zi2.find_spec(TESTMOD)
     602          self.assertIsNotNone(spec)
     603          self.assertIsInstance(spec.loader, zipimport.zipimporter)
     604          self.assertFalse(spec.loader.is_package(TESTMOD))
     605          load_mod = importlib.util.module_from_spec(spec)
     606          spec.loader.exec_module(load_mod)
     607          self.assertEqual(
     608              spec.loader.get_filename(TESTMOD), load_mod.__file__)
     609  
     610          mod_path = TESTPACK2 + os.sep + TESTMOD
     611          mod_name = module_path_to_dotted_name(mod_path)
     612          mod = importlib.import_module(mod_name)
     613          self.assertTrue(mod_name in sys.modules)
     614          self.assertIsNone(zi.get_source(TESTPACK2))
     615          self.assertIsNone(zi.get_source(mod_path))
     616          self.assertEqual(zi.get_filename(mod_path), mod.__file__)
     617          # To pass in the module name instead of the path, we must use the
     618          # right importer.
     619          loader = mod.__loader__
     620          self.assertIsNone(loader.get_source(mod_name))
     621          self.assertEqual(loader.get_filename(mod_name), mod.__file__)
     622  
     623      def testGetData(self):
     624          self.addCleanup(os_helper.unlink, TEMP_ZIP)
     625          with ZipFile(TEMP_ZIP, "w") as z:
     626              z.compression = self.compression
     627              name = "testdata.dat"
     628              data = bytes(x for x in range(256))
     629              z.writestr(name, data)
     630  
     631          zi = zipimport.zipimporter(TEMP_ZIP)
     632          self.assertEqual(data, zi.get_data(name))
     633          self.assertIn('zipimporter object', repr(zi))
     634  
     635      def testImporterAttr(self):
     636          src = """if 1:  # indent hack
     637          def get_file():
     638              return __file__
     639          if __loader__.get_data("some.data") != b"some data":
     640              raise AssertionError("bad data")\n"""
     641          pyc = make_pyc(compile(src, "<???>", "exec"), NOW, len(src))
     642          files = {TESTMOD + pyc_ext: (NOW, pyc),
     643                   "some.data": (NOW, "some data")}
     644          self.doTest(pyc_ext, files, TESTMOD)
     645  
     646      def testDefaultOptimizationLevel(self):
     647          # zipimport should use the default optimization level (#28131)
     648          src = """if 1:  # indent hack
     649          def test(val):
     650              assert(val)
     651              return val\n"""
     652          files = {TESTMOD + '.py': (NOW, src)}
     653          self.makeZip(files)
     654          sys.path.insert(0, TEMP_ZIP)
     655          mod = importlib.import_module(TESTMOD)
     656          self.assertEqual(mod.test(1), 1)
     657          if __debug__:
     658              self.assertRaises(AssertionError, mod.test, False)
     659          else:
     660              self.assertEqual(mod.test(0), 0)
     661  
     662      def testImport_WithStuff(self):
     663          # try importing from a zipfile which contains additional
     664          # stuff at the beginning of the file
     665          files = {TESTMOD + ".py": (NOW, test_src)}
     666          self.doTest(".py", files, TESTMOD,
     667                      stuff=b"Some Stuff"*31)
     668  
     669      def assertModuleSource(self, module):
     670          self.assertEqual(inspect.getsource(module), test_src)
     671  
     672      def testGetSource(self):
     673          files = {TESTMOD + ".py": (NOW, test_src)}
     674          self.doTest(".py", files, TESTMOD, call=self.assertModuleSource)
     675  
     676      def testGetCompiledSource(self):
     677          pyc = make_pyc(compile(test_src, "<???>", "exec"), NOW, len(test_src))
     678          files = {TESTMOD + ".py": (NOW, test_src),
     679                   TESTMOD + pyc_ext: (NOW, pyc)}
     680          self.doTest(pyc_ext, files, TESTMOD, call=self.assertModuleSource)
     681  
     682      def runDoctest(self, callback):
     683          files = {TESTMOD + ".py": (NOW, test_src),
     684                   "xyz.txt": (NOW, ">>> log.append(True)\n")}
     685          self.doTest(".py", files, TESTMOD, call=callback)
     686  
     687      def doDoctestFile(self, module):
     688          log = []
     689          old_master, doctest.master = doctest.master, None
     690          try:
     691              doctest.testfile(
     692                  'xyz.txt', package=module, module_relative=True,
     693                  globs=locals()
     694              )
     695          finally:
     696              doctest.master = old_master
     697          self.assertEqual(log,[True])
     698  
     699      def testDoctestFile(self):
     700          self.runDoctest(self.doDoctestFile)
     701  
     702      def doDoctestSuite(self, module):
     703          log = []
     704          doctest.DocFileTest(
     705              'xyz.txt', package=module, module_relative=True,
     706              globs=locals()
     707          ).run()
     708          self.assertEqual(log,[True])
     709  
     710      def testDoctestSuite(self):
     711          self.runDoctest(self.doDoctestSuite)
     712  
     713      def doTraceback(self, module):
     714          try:
     715              module.do_raise()
     716          except Exception as e:
     717              tb = e.__traceback__.tb_next
     718  
     719              f,lno,n,line = extract_tb(tb, 1)[0]
     720              self.assertEqual(line, raise_src.strip())
     721  
     722              f,lno,n,line = extract_stack(tb.tb_frame, 1)[0]
     723              self.assertEqual(line, raise_src.strip())
     724  
     725              s = io.StringIO()
     726              print_tb(tb, 1, s)
     727              self.assertTrue(s.getvalue().endswith(
     728                  '    def do_raise(): raise TypeError\n'
     729                  '' if support.has_no_debug_ranges() else
     730                  '                    ^^^^^^^^^^^^^^^\n'
     731              ))
     732          else:
     733              raise AssertionError("This ought to be impossible")
     734  
     735      def testTraceback(self):
     736          files = {TESTMOD + ".py": (NOW, raise_src)}
     737          self.doTest(None, files, TESTMOD, call=self.doTraceback)
     738  
     739      @unittest.skipIf(os_helper.TESTFN_UNENCODABLE is None,
     740                       "need an unencodable filename")
     741      def testUnencodable(self):
     742          filename = os_helper.TESTFN_UNENCODABLE + ".zip"
     743          self.addCleanup(os_helper.unlink, filename)
     744          with ZipFile(filename, "w") as z:
     745              zinfo = ZipInfo(TESTMOD + ".py", time.localtime(NOW))
     746              zinfo.compress_type = self.compression
     747              z.writestr(zinfo, test_src)
     748          spec = zipimport.zipimporter(filename).find_spec(TESTMOD)
     749          mod = importlib.util.module_from_spec(spec)
     750          spec.loader.exec_module(mod)
     751  
     752      def testBytesPath(self):
     753          filename = os_helper.TESTFN + ".zip"
     754          self.addCleanup(os_helper.unlink, filename)
     755          with ZipFile(filename, "w") as z:
     756              zinfo = ZipInfo(TESTMOD + ".py", time.localtime(NOW))
     757              zinfo.compress_type = self.compression
     758              z.writestr(zinfo, test_src)
     759  
     760          zipimport.zipimporter(filename)
     761          with self.assertRaises(TypeError):
     762              zipimport.zipimporter(os.fsencode(filename))
     763          with self.assertRaises(TypeError):
     764              zipimport.zipimporter(bytearray(os.fsencode(filename)))
     765          with self.assertRaises(TypeError):
     766              zipimport.zipimporter(memoryview(os.fsencode(filename)))
     767  
     768      def testComment(self):
     769          files = {TESTMOD + ".py": (NOW, test_src)}
     770          self.doTest(".py", files, TESTMOD, comment=b"comment")
     771  
     772      def testBeginningCruftAndComment(self):
     773          files = {TESTMOD + ".py": (NOW, test_src)}
     774          self.doTest(".py", files, TESTMOD, stuff=b"cruft" * 64, comment=b"hi")
     775  
     776      def testLargestPossibleComment(self):
     777          files = {TESTMOD + ".py": (NOW, test_src)}
     778          self.doTest(".py", files, TESTMOD, comment=b"c" * ((1 << 16) - 1))
     779  
     780  
     781  @support.requires_zlib()
     782  class ESC[4;38;5;81mCompressedZipImportTestCase(ESC[4;38;5;149mUncompressedZipImportTestCase):
     783      compression = ZIP_DEFLATED
     784  
     785  
     786  class ESC[4;38;5;81mBadFileZipImportTestCase(ESC[4;38;5;149munittestESC[4;38;5;149m.ESC[4;38;5;149mTestCase):
     787      def assertZipFailure(self, filename):
     788          self.assertRaises(zipimport.ZipImportError,
     789                            zipimport.zipimporter, filename)
     790  
     791      def testNoFile(self):
     792          self.assertZipFailure('AdfjdkFJKDFJjdklfjs')
     793  
     794      def testEmptyFilename(self):
     795          self.assertZipFailure('')
     796  
     797      def testBadArgs(self):
     798          self.assertRaises(TypeError, zipimport.zipimporter, None)
     799          self.assertRaises(TypeError, zipimport.zipimporter, TESTMOD, kwd=None)
     800          self.assertRaises(TypeError, zipimport.zipimporter,
     801                            list(os.fsencode(TESTMOD)))
     802  
     803      def testFilenameTooLong(self):
     804          self.assertZipFailure('A' * 33000)
     805  
     806      def testEmptyFile(self):
     807          os_helper.unlink(TESTMOD)
     808          os_helper.create_empty_file(TESTMOD)
     809          self.assertZipFailure(TESTMOD)
     810  
     811      @unittest.skipIf(support.is_wasi, "mode 000 not supported.")
     812      def testFileUnreadable(self):
     813          os_helper.unlink(TESTMOD)
     814          fd = os.open(TESTMOD, os.O_CREAT, 000)
     815          try:
     816              os.close(fd)
     817  
     818              with self.assertRaises(zipimport.ZipImportError) as cm:
     819                  zipimport.zipimporter(TESTMOD)
     820          finally:
     821              # If we leave "the read-only bit" set on Windows, nothing can
     822              # delete TESTMOD, and later tests suffer bogus failures.
     823              os.chmod(TESTMOD, 0o666)
     824              os_helper.unlink(TESTMOD)
     825  
     826      def testNotZipFile(self):
     827          os_helper.unlink(TESTMOD)
     828          fp = open(TESTMOD, 'w+')
     829          fp.write('a' * 22)
     830          fp.close()
     831          self.assertZipFailure(TESTMOD)
     832  
     833      # XXX: disabled until this works on Big-endian machines
     834      def _testBogusZipFile(self):
     835          os_helper.unlink(TESTMOD)
     836          fp = open(TESTMOD, 'w+')
     837          fp.write(struct.pack('=I', 0x06054B50))
     838          fp.write('a' * 18)
     839          fp.close()
     840          z = zipimport.zipimporter(TESTMOD)
     841  
     842          try:
     843              with warnings.catch_warnings():
     844                  warnings.simplefilter("ignore", DeprecationWarning)
     845                  self.assertRaises(TypeError, z.load_module, None)
     846              self.assertRaises(TypeError, z.find_module, None)
     847              self.assertRaises(TypeError, z.find_spec, None)
     848              self.assertRaises(TypeError, z.exec_module, None)
     849              self.assertRaises(TypeError, z.is_package, None)
     850              self.assertRaises(TypeError, z.get_code, None)
     851              self.assertRaises(TypeError, z.get_data, None)
     852              self.assertRaises(TypeError, z.get_source, None)
     853  
     854              error = zipimport.ZipImportError
     855              self.assertIsNone(z.find_module('abc'))
     856              self.assertIsNone(z.find_spec('abc'))
     857  
     858              with warnings.catch_warnings():
     859                  warnings.simplefilter("ignore", DeprecationWarning)
     860                  self.assertRaises(error, z.load_module, 'abc')
     861              self.assertRaises(error, z.get_code, 'abc')
     862              self.assertRaises(OSError, z.get_data, 'abc')
     863              self.assertRaises(error, z.get_source, 'abc')
     864              self.assertRaises(error, z.is_package, 'abc')
     865          finally:
     866              zipimport._zip_directory_cache.clear()
     867  
     868  
     869  def tearDownModule():
     870      os_helper.unlink(TESTMOD)
     871  
     872  
     873  if __name__ == "__main__":
     874      unittest.main()