@@ -46,6 +46,27 @@ def module_path_to_dotted_name(path):
4646pyc_ext = ('.pyc' if __debug__ else '.pyo' )
4747
4848
49+ def _write_zip_package (zipname , files ,
50+ data_to_prepend = b"" , compression = ZIP_STORED ):
51+ z = ZipFile (zipname , "w" )
52+ try :
53+ for name , (mtime , data ) in files .items ():
54+ zinfo = ZipInfo (name , time .localtime (mtime ))
55+ zinfo .compress_type = compression
56+ z .writestr (zinfo , data )
57+ finally :
58+ z .close ()
59+
60+ if data_to_prepend :
61+ # Prepend data to the start of the zipfile
62+ with open (zipname , "rb" ) as f :
63+ zip_data = f .read ()
64+
65+ with open (zipname , "wb" ) as f :
66+ f .write (data_to_prepend )
67+ f .write (zip_data )
68+
69+
4970class UncompressedZipImportTestCase (ImportHooksBaseTestCase ):
5071
5172 compression = ZIP_STORED
@@ -58,23 +79,9 @@ def setUp(self):
5879 ImportHooksBaseTestCase .setUp (self )
5980
6081 def doTest (self , expected_ext , files , * modules , ** kw ):
61- z = ZipFile (TEMP_ZIP , "w" )
82+ _write_zip_package (TEMP_ZIP , files , data_to_prepend = kw .get ("stuff" ),
83+ compression = self .compression )
6284 try :
63- for name , (mtime , data ) in files .items ():
64- zinfo = ZipInfo (name , time .localtime (mtime ))
65- zinfo .compress_type = self .compression
66- z .writestr (zinfo , data )
67- z .close ()
68-
69- stuff = kw .get ("stuff" , None )
70- if stuff is not None :
71- # Prepend 'stuff' to the start of the zipfile
72- with open (TEMP_ZIP , "rb" ) as f :
73- data = f .read ()
74- with open (TEMP_ZIP , "wb" ) as f :
75- f .write (stuff )
76- f .write (data )
77-
7885 sys .path .insert (0 , TEMP_ZIP )
7986
8087 mod = __import__ ("." .join (modules ), globals (), locals (),
@@ -89,7 +96,8 @@ def doTest(self, expected_ext, files, *modules, **kw):
8996 self .assertEqual (file , os .path .join (TEMP_ZIP ,
9097 * modules ) + expected_ext )
9198 finally :
92- z .close ()
99+ while TEMP_ZIP in sys .path :
100+ sys .path .remove (TEMP_ZIP )
93101 os .remove (TEMP_ZIP )
94102
95103 def testAFakeZlib (self ):
@@ -395,10 +403,67 @@ class CompressedZipImportTestCase(UncompressedZipImportTestCase):
395403 compression = ZIP_DEFLATED
396404
397405
406+ class ZipFileModifiedAfterImportTestCase (ImportHooksBaseTestCase ):
407+ def setUp (self ):
408+ zipimport ._zip_directory_cache .clear ()
409+ zipimport ._zip_stat_cache .clear ()
410+ ImportHooksBaseTestCase .setUp (self )
411+
412+ def tearDown (self ):
413+ ImportHooksBaseTestCase .tearDown (self )
414+ if os .path .exists (TEMP_ZIP ):
415+ os .remove (TEMP_ZIP )
416+
417+ def testZipFileChangesAfterFirstImport (self ):
418+ """Alter the zip file after caching its index and try an import."""
419+ packdir = TESTPACK + os .sep
420+ files = {packdir + "__init__" + pyc_ext : (NOW , test_pyc ),
421+ packdir + TESTMOD + ".py" : (NOW , "test_value = 38\n " ),
422+ "ziptest_a.py" : (NOW , "test_value = 23\n " ),
423+ "ziptest_b.py" : (NOW , "test_value = 42\n " ),
424+ "ziptest_c.py" : (NOW , "test_value = 1337\n " )}
425+ zipfile_path = TEMP_ZIP
426+ _write_zip_package (zipfile_path , files )
427+ self .assertTrue (os .path .exists (zipfile_path ))
428+ sys .path .insert (0 , zipfile_path )
429+
430+ # Import something out of the zipfile and confirm it is correct.
431+ testmod = __import__ (TESTPACK + "." + TESTMOD ,
432+ globals (), locals (), ["__dummy__" ])
433+ self .assertEqual (testmod .test_value , 38 )
434+ # Import something else out of the zipfile and confirm it is correct.
435+ ziptest_b = __import__ ("ziptest_b" , globals (), locals (), ["test_value" ])
436+ self .assertEqual (ziptest_b .test_value , 42 )
437+
438+ # Truncate and fill the zip file with non-zip garbage.
439+ with open (zipfile_path , "rb" ) as orig_zip_file :
440+ orig_zip_file_contents = orig_zip_file .read ()
441+ with open (zipfile_path , "wb" ) as byebye_valid_zip_file :
442+ byebye_valid_zip_file .write (b"Tear down this wall!\n " * 1987 )
443+ # Now that the zipfile has been replaced, import something else from it
444+ # which should fail as the file contents are now garbage.
445+ with self .assertRaises (ImportError ):
446+ ziptest_a = __import__ ("ziptest_a" , globals (), locals (),
447+ ["test_value" ])
448+ self .assertEqual (ziptest_a .test_value , 23 )
449+
450+ # Now lets make it a valid zipfile that has some garbage at the start.
451+ # This alters all of the offsets within the file
452+ with open (zipfile_path , "wb" ) as new_zip_file :
453+ new_zip_file .write (b"X" * 1991 ) # The year Python was created.
454+ new_zip_file .write (orig_zip_file_contents )
455+
456+ # Now that the zip file has been "restored" to a valid but different
457+ # zipfile the zipimporter should *successfully* re-read the new zip
458+ # file's end of file central index and be able to import from it again.
459+ ziptest_c = __import__ ("ziptest_c" , globals (), locals (), ["test_value" ])
460+ self .assertEqual (ziptest_c .test_value , 1337 )
461+
462+
398463class BadFileZipImportTestCase (unittest .TestCase ):
399464 def assertZipFailure (self , filename ):
400- self .assertRaises (zipimport .ZipImportError ,
401- zipimport .zipimporter , filename )
465+ with self .assertRaises (zipimport .ZipImportError ):
466+ zipimport .zipimporter ( filename )
402467
403468 def testNoFile (self ):
404469 self .assertZipFailure ('AdfjdkFJKDFJjdklfjs' )
@@ -472,6 +537,7 @@ def test_main():
472537 UncompressedZipImportTestCase ,
473538 CompressedZipImportTestCase ,
474539 BadFileZipImportTestCase ,
540+ ZipFileModifiedAfterImportTestCase ,
475541 )
476542 finally :
477543 support .unlink (TESTMOD )
0 commit comments