test_zipimport.py 34 KB

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