@@ -318,6 +318,81 @@ def testSubNamespacePackage(self):
318318 packdir2 + TESTMOD + pyc_ext : test_pyc }
319319 self .doTest (pyc_ext , files , TESTPACK , TESTPACK2 , TESTMOD )
320320
321+ def testPackageExplicitDirectories (self ):
322+ # Test explicit namespace packages with explicit directory entries.
323+ self .addCleanup (os_helper .unlink , TEMP_ZIP )
324+ with ZipFile (TEMP_ZIP , 'w' , compression = self .compression ) as z :
325+ z .mkdir ('a' )
326+ z .writestr ('a/__init__.py' , test_src )
327+ z .mkdir ('a/b' )
328+ z .writestr ('a/b/__init__.py' , test_src )
329+ z .mkdir ('a/b/c' )
330+ z .writestr ('a/b/c/__init__.py' , test_src )
331+ z .writestr ('a/b/c/d.py' , test_src )
332+ self ._testPackage (initfile = '__init__.py' )
333+
334+ def testPackageImplicitDirectories (self ):
335+ # Test explicit namespace packages without explicit directory entries.
336+ self .addCleanup (os_helper .unlink , TEMP_ZIP )
337+ with ZipFile (TEMP_ZIP , 'w' , compression = self .compression ) as z :
338+ z .writestr ('a/__init__.py' , test_src )
339+ z .writestr ('a/b/__init__.py' , test_src )
340+ z .writestr ('a/b/c/__init__.py' , test_src )
341+ z .writestr ('a/b/c/d.py' , test_src )
342+ self ._testPackage (initfile = '__init__.py' )
343+
344+ def testNamespacePackageExplicitDirectories (self ):
345+ # Test implicit namespace packages with explicit directory entries.
346+ self .addCleanup (os_helper .unlink , TEMP_ZIP )
347+ with ZipFile (TEMP_ZIP , 'w' , compression = self .compression ) as z :
348+ z .mkdir ('a' )
349+ z .mkdir ('a/b' )
350+ z .mkdir ('a/b/c' )
351+ z .writestr ('a/b/c/d.py' , test_src )
352+ self ._testPackage (initfile = None )
353+
354+ def testNamespacePackageImplicitDirectories (self ):
355+ # Test implicit namespace packages without explicit directory entries.
356+ self .addCleanup (os_helper .unlink , TEMP_ZIP )
357+ with ZipFile (TEMP_ZIP , 'w' , compression = self .compression ) as z :
358+ z .writestr ('a/b/c/d.py' , test_src )
359+ self ._testPackage (initfile = None )
360+
361+ def _testPackage (self , initfile ):
362+ zi = zipimport .zipimporter (os .path .join (TEMP_ZIP , 'a' ))
363+ if initfile is None :
364+ # XXX Should it work?
365+ self .assertRaises (zipimport .ZipImportError , zi .is_package , 'b' )
366+ self .assertRaises (zipimport .ZipImportError , zi .get_source , 'b' )
367+ self .assertRaises (zipimport .ZipImportError , zi .get_code , 'b' )
368+ else :
369+ self .assertTrue (zi .is_package ('b' ))
370+ self .assertEqual (zi .get_source ('b' ), test_src )
371+ self .assertEqual (zi .get_code ('b' ).co_filename ,
372+ os .path .join (TEMP_ZIP , 'a' , 'b' , initfile ))
373+
374+ sys .path .insert (0 , TEMP_ZIP )
375+ self .assertNotIn ('a' , sys .modules )
376+
377+ mod = importlib .import_module (f'a.b' )
378+ self .assertIn ('a' , sys .modules )
379+ self .assertIs (sys .modules ['a.b' ], mod )
380+ if initfile is None :
381+ self .assertIsNone (mod .__file__ )
382+ else :
383+ self .assertEqual (mod .__file__ ,
384+ os .path .join (TEMP_ZIP , 'a' , 'b' , initfile ))
385+ self .assertEqual (len (mod .__path__ ), 1 , mod .__path__ )
386+ self .assertEqual (mod .__path__ [0 ], os .path .join (TEMP_ZIP , 'a' , 'b' ))
387+
388+ mod2 = importlib .import_module (f'a.b.c.d' )
389+ self .assertIn ('a.b.c' , sys .modules )
390+ self .assertIn ('a.b.c.d' , sys .modules )
391+ self .assertIs (sys .modules ['a.b.c.d' ], mod2 )
392+ self .assertIs (mod .c .d , mod2 )
393+ self .assertEqual (mod2 .__file__ ,
394+ os .path .join (TEMP_ZIP , 'a' , 'b' , 'c' , 'd.py' ))
395+
321396 def testMixedNamespacePackage (self ):
322397 # Test implicit namespace packages spread between a
323398 # real filesystem and a zip archive.
@@ -535,21 +610,22 @@ def testInvalidateCaches(self):
535610 packdir2 + "__init__" + pyc_ext : test_pyc ,
536611 packdir2 + TESTMOD + pyc_ext : test_pyc ,
537612 "spam" + pyc_ext : test_pyc }
613+ extra_files = [packdir , packdir2 ]
538614 self .makeZip (files , file_comment = b"spam" )
539615
540616 zi = zipimport .zipimporter (TEMP_ZIP )
541- self .assertEqual (zi ._get_files (). keys ( ), files . keys ( ))
617+ self .assertEqual (sorted ( zi ._get_files ()), sorted ([ * files , * extra_files ] ))
542618 # Check that the file information remains accurate after reloading
543619 zi .invalidate_caches ()
544- self .assertEqual (zi ._get_files (). keys ( ), files . keys ( ))
620+ self .assertEqual (sorted ( zi ._get_files ()), sorted ([ * files , * extra_files ] ))
545621 # Add a new file to the ZIP archive
546622 newfile = {"spam2" + pyc_ext : test_pyc }
547623 files .update (newfile )
548624 with ZipFile (TEMP_ZIP , "a" , compression = self .compression ) as z :
549625 self .writeZip (z , newfile , file_comment = b"spam" )
550626 # Check that we can detect the new file after invalidating the cache
551627 zi .invalidate_caches ()
552- self .assertEqual (zi ._get_files (). keys ( ), files . keys ( ))
628+ self .assertEqual (sorted ( zi ._get_files ()), sorted ([ * files , * extra_files ] ))
553629 spec = zi .find_spec ('spam2' )
554630 self .assertIsNotNone (spec )
555631 self .assertIsInstance (spec .loader , zipimport .zipimporter )
@@ -567,13 +643,14 @@ def testInvalidateCachesWithMultipleZipimports(self):
567643 packdir2 + "__init__" + pyc_ext : test_pyc ,
568644 packdir2 + TESTMOD + pyc_ext : test_pyc ,
569645 "spam" + pyc_ext : test_pyc }
646+ extra_files = [packdir , packdir2 ]
570647 self .makeZip (files , file_comment = b"spam" )
571648
572649 zi = zipimport .zipimporter (TEMP_ZIP )
573- self .assertEqual (zi ._get_files (). keys ( ), files . keys ( ))
650+ self .assertEqual (sorted ( zi ._get_files ()), sorted ([ * files , * extra_files ] ))
574651 # Zipimporter for the same path.
575652 zi2 = zipimport .zipimporter (TEMP_ZIP )
576- self .assertEqual (zi2 ._get_files (). keys ( ), files . keys ( ))
653+ self .assertEqual (sorted ( zi2 ._get_files ()), sorted ([ * files , * extra_files ] ))
577654 # Add a new file to the ZIP archive to make the cache wrong.
578655 newfile = {"spam2" + pyc_ext : test_pyc }
579656 files .update (newfile )
@@ -582,7 +659,7 @@ def testInvalidateCachesWithMultipleZipimports(self):
582659 # Invalidate the cache of the first zipimporter.
583660 zi .invalidate_caches ()
584661 # Check that the second zipimporter detects the new file and isn't using a stale cache.
585- self .assertEqual (zi2 ._get_files (). keys ( ), files . keys ( ))
662+ self .assertEqual (sorted ( zi2 ._get_files ()), sorted ([ * files , * extra_files ] ))
586663 spec = zi2 .find_spec ('spam2' )
587664 self .assertIsNotNone (spec )
588665 self .assertIsInstance (spec .loader , zipimport .zipimporter )
@@ -638,17 +715,33 @@ def testZipImporterMethodsInSubDirectory(self):
638715 self .assertIsNone (loader .get_source (mod_name ))
639716 self .assertEqual (loader .get_filename (mod_name ), mod .__file__ )
640717
641- def testGetData (self ):
718+ def testGetDataExplicitDirectories (self ):
642719 self .addCleanup (os_helper .unlink , TEMP_ZIP )
643- with ZipFile (TEMP_ZIP , "w" ) as z :
644- z .compression = self .compression
645- name = "testdata.dat"
646- data = bytes (x for x in range (256 ))
647- z .writestr (name , data )
648-
649- zi = zipimport .zipimporter (TEMP_ZIP )
650- self .assertEqual (data , zi .get_data (name ))
651- self .assertIn ('zipimporter object' , repr (zi ))
720+ with ZipFile (TEMP_ZIP , 'w' , compression = self .compression ) as z :
721+ z .mkdir ('a' )
722+ z .mkdir ('a/b' )
723+ z .mkdir ('a/b/c' )
724+ data = bytes (range (256 ))
725+ z .writestr ('a/b/c/testdata.dat' , data )
726+ self ._testGetData ()
727+
728+ def testGetDataImplicitDirectories (self ):
729+ self .addCleanup (os_helper .unlink , TEMP_ZIP )
730+ with ZipFile (TEMP_ZIP , 'w' , compression = self .compression ) as z :
731+ data = bytes (range (256 ))
732+ z .writestr ('a/b/c/testdata.dat' , data )
733+ self ._testGetData ()
734+
735+ def _testGetData (self ):
736+ zi = zipimport .zipimporter (os .path .join (TEMP_ZIP , 'ignored' ))
737+ pathname = os .path .join ('a' , 'b' , 'c' , 'testdata.dat' )
738+ data = bytes (range (256 ))
739+ self .assertEqual (zi .get_data (pathname ), data )
740+ self .assertEqual (zi .get_data (os .path .join (TEMP_ZIP , pathname )), data )
741+ self .assertEqual (zi .get_data (os .path .join ('a' , 'b' , '' )), b'' )
742+ self .assertEqual (zi .get_data (os .path .join (TEMP_ZIP , 'a' , 'b' , '' )), b'' )
743+ self .assertRaises (OSError , zi .get_data , os .path .join ('a' , 'b' ))
744+ self .assertRaises (OSError , zi .get_data , os .path .join (TEMP_ZIP , 'a' , 'b' ))
652745
653746 def testImporterAttr (self ):
654747 src = """if 1: # indent hack
@@ -742,15 +835,15 @@ def doTraceback(self, module):
742835
743836 s = io .StringIO ()
744837 print_tb (tb , 1 , s )
745- self .assertTrue (s .getvalue (). endswith (
838+ self .assertEndsWith (s .getvalue (),
746839 ' def do_raise(): raise TypeError\n '
747840 '' if support .has_no_debug_ranges () else
748841 ' ^^^^^^^^^^^^^^^\n '
749- ))
842+ )
750843 else :
751844 raise AssertionError ("This ought to be impossible" )
752845
753- @unittest .expectedFailure # TODO: RUSTPYTHON; empty caret lines from equal col/end_col
846+ @unittest .expectedFailure # TODO: RUSTPYTHON; empty caret lines from equal col/end_col
754847 def testTraceback (self ):
755848 files = {TESTMOD + ".py" : raise_src }
756849 self .doTest (None , files , TESTMOD , call = self .doTraceback )
@@ -788,15 +881,17 @@ def testLargestPossibleComment(self):
788881 files = {TESTMOD + ".py" : test_src }
789882 self .doTest (".py" , files , TESTMOD , comment = b"c" * ((1 << 16 ) - 1 ))
790883
884+ @support .requires_resource ('cpu' )
791885 def testZip64 (self ):
792886 files = self .getZip64Files ()
793887 self .doTest (".py" , files , "f6" )
794888
889+ @support .requires_resource ('cpu' )
795890 def testZip64CruftAndComment (self ):
796891 files = self .getZip64Files ()
797892 self .doTest (".py" , files , "f65536" , comment = b"c" * ((1 << 16 ) - 1 ))
798893
799- @unittest .skip (' TODO: RUSTPYTHON; (intermittent success/failures); ValueError: name="RustPython/crates/pylib/Lib/test/zipimport_data/sparse-zip64-c0-0x000000000.part" does not fit expected pattern.' )
894+ @unittest .skip (" TODO: RUSTPYTHON; (intermittent success/failures); ValueError: name=\ " RustPython/crates/pylib/Lib/test/zipimport_data/sparse-zip64-c0-0x000000000.part\ " does not fit expected pattern." )
800895 def testZip64LargeFile (self ):
801896 support .requires (
802897 "largefile" ,
0 commit comments