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