Skip to content

Commit 178446c

Browse files
CPython developersyouknowone
authored andcommitted
Update zipimport from CPython 3.10.5
1 parent 0437f13 commit 178446c

File tree

2 files changed

+96
-116
lines changed

2 files changed

+96
-116
lines changed

Lib/test/test_zipimport.py

Lines changed: 21 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -467,15 +467,15 @@ def testZipImporterMethods(self):
467467
# PEP 302
468468
with warnings.catch_warnings():
469469
warnings.simplefilter("ignore", DeprecationWarning)
470-
find_mod = zi.find_module('spam')
471-
self.assertIsNotNone(find_mod)
472-
self.assertIsInstance(find_mod, zipimport.zipimporter)
473-
self.assertFalse(find_mod.is_package('spam'))
474-
load_mod = find_mod.load_module('spam')
475-
self.assertEqual(find_mod.get_filename('spam'), load_mod.__file__)
476-
477-
mod = zi.load_module(TESTPACK)
478-
self.assertEqual(zi.get_filename(TESTPACK), mod.__file__)
470+
find_mod = zi.find_module('spam')
471+
self.assertIsNotNone(find_mod)
472+
self.assertIsInstance(find_mod, zipimport.zipimporter)
473+
self.assertFalse(find_mod.is_package('spam'))
474+
load_mod = find_mod.load_module('spam')
475+
self.assertEqual(find_mod.get_filename('spam'), load_mod.__file__)
476+
477+
mod = zi.load_module(TESTPACK)
478+
self.assertEqual(zi.get_filename(TESTPACK), mod.__file__)
479479

480480
# PEP 451
481481
spec = zi.find_spec('spam')
@@ -584,8 +584,8 @@ def testZipImporterMethodsInSubDirectory(self):
584584
# PEP 302
585585
with warnings.catch_warnings():
586586
warnings.simplefilter("ignore", DeprecationWarning)
587-
mod = zi.load_module(TESTPACK2)
588-
self.assertEqual(zi.get_filename(TESTPACK2), mod.__file__)
587+
mod = zi.load_module(TESTPACK2)
588+
self.assertEqual(zi.get_filename(TESTPACK2), mod.__file__)
589589
# PEP 451
590590
spec = zi.find_spec(TESTPACK2)
591591
mod = importlib.util.module_from_spec(spec)
@@ -600,13 +600,13 @@ def testZipImporterMethodsInSubDirectory(self):
600600
# PEP 302
601601
with warnings.catch_warnings():
602602
warnings.simplefilter("ignore", DeprecationWarning)
603-
find_mod_dotted = zi2.find_module(TESTMOD)
604-
self.assertIsNotNone(find_mod_dotted)
605-
self.assertIsInstance(find_mod_dotted, zipimport.zipimporter)
606-
self.assertFalse(zi2.is_package(TESTMOD))
607-
load_mod = find_mod_dotted.load_module(TESTMOD)
608-
self.assertEqual(
609-
find_mod_dotted.get_filename(TESTMOD), load_mod.__file__)
603+
find_mod_dotted = zi2.find_module(TESTMOD)
604+
self.assertIsNotNone(find_mod_dotted)
605+
self.assertIsInstance(find_mod_dotted, zipimport.zipimporter)
606+
self.assertFalse(zi2.is_package(TESTMOD))
607+
load_mod = find_mod_dotted.load_module(TESTMOD)
608+
self.assertEqual(
609+
find_mod_dotted.get_filename(TESTMOD), load_mod.__file__)
610610

611611
# PEP 451
612612
spec = zi2.find_spec(TESTMOD)
@@ -846,7 +846,7 @@ def _testBogusZipFile(self):
846846
try:
847847
with warnings.catch_warnings():
848848
warnings.simplefilter("ignore", DeprecationWarning)
849-
self.assertRaises(TypeError, z.load_module, None)
849+
self.assertRaises(TypeError, z.load_module, None)
850850
self.assertRaises(TypeError, z.find_module, None)
851851
self.assertRaises(TypeError, z.find_spec, None)
852852
self.assertRaises(TypeError, z.exec_module, None)
@@ -861,7 +861,7 @@ def _testBogusZipFile(self):
861861

862862
with warnings.catch_warnings():
863863
warnings.simplefilter("ignore", DeprecationWarning)
864-
self.assertRaises(error, z.load_module, 'abc')
864+
self.assertRaises(error, z.load_module, 'abc')
865865
self.assertRaises(error, z.get_code, 'abc')
866866
self.assertRaises(OSError, z.get_data, 'abc')
867867
self.assertRaises(error, z.get_source, 'abc')
@@ -871,7 +871,7 @@ def _testBogusZipFile(self):
871871

872872

873873
def tearDownModule():
874-
os_helper.unlink(TESTMOD)
874+
os_helper.unlink(TESTMOD)
875875

876876

877877
if __name__ == "__main__":

Lib/zipimport.py

Lines changed: 75 additions & 95 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
import marshal # for loads
2323
import sys # for modules
2424
import time # for mktime
25+
import _warnings # For warn()
2526

2627
__all__ = ['ZipImportError', 'zipimporter']
2728

@@ -42,7 +43,7 @@ class ZipImportError(ImportError):
4243
STRING_END_ARCHIVE = b'PK\x05\x06'
4344
MAX_COMMENT_LEN = (1 << 16) - 1
4445

45-
class zipimporter:
46+
class zipimporter(_bootstrap_external._LoaderBasics):
4647
"""zipimporter(archivepath) -> zipimporter object
4748
4849
Create a new zipimporter instance. 'archivepath' must be a path to
@@ -115,7 +116,12 @@ def find_loader(self, fullname, path=None):
115116
full path name if it's possibly a portion of a namespace package,
116117
or None otherwise. The optional 'path' argument is ignored -- it's
117118
there for compatibility with the importer protocol.
119+
120+
Deprecated since Python 3.10. Use find_spec() instead.
118121
"""
122+
_warnings.warn("zipimporter.find_loader() is deprecated and slated for "
123+
"removal in Python 3.12; use find_spec() instead",
124+
DeprecationWarning)
119125
mi = _get_module_info(self, fullname)
120126
if mi is not None:
121127
# This is a module or package.
@@ -146,15 +152,46 @@ def find_module(self, fullname, path=None):
146152
instance itself if the module was found, or None if it wasn't.
147153
The optional 'path' argument is ignored -- it's there for compatibility
148154
with the importer protocol.
155+
156+
Deprecated since Python 3.10. Use find_spec() instead.
149157
"""
158+
_warnings.warn("zipimporter.find_module() is deprecated and slated for "
159+
"removal in Python 3.12; use find_spec() instead",
160+
DeprecationWarning)
150161
return self.find_loader(fullname, path)[0]
151162

163+
def find_spec(self, fullname, target=None):
164+
"""Create a ModuleSpec for the specified module.
165+
166+
Returns None if the module cannot be found.
167+
"""
168+
module_info = _get_module_info(self, fullname)
169+
if module_info is not None:
170+
return _bootstrap.spec_from_loader(fullname, self, is_package=module_info)
171+
else:
172+
# Not a module or regular package. See if this is a directory, and
173+
# therefore possibly a portion of a namespace package.
174+
175+
# We're only interested in the last path component of fullname
176+
# earlier components are recorded in self.prefix.
177+
modpath = _get_module_path(self, fullname)
178+
if _is_dir(self, modpath):
179+
# This is possibly a portion of a namespace
180+
# package. Return the string representing its path,
181+
# without a trailing separator.
182+
path = f'{self.archive}{path_sep}{modpath}'
183+
spec = _bootstrap.ModuleSpec(name=fullname, loader=None,
184+
is_package=True)
185+
spec.submodule_search_locations.append(path)
186+
return spec
187+
else:
188+
return None
152189

153190
def get_code(self, fullname):
154191
"""get_code(fullname) -> code object.
155192
156193
Return the code object for the specified module. Raise ZipImportError
157-
if the module couldn't be found.
194+
if the module couldn't be imported.
158195
"""
159196
code, ispackage, modpath = _get_module_code(self, fullname)
160197
return code
@@ -184,7 +221,8 @@ def get_data(self, pathname):
184221
def get_filename(self, fullname):
185222
"""get_filename(fullname) -> filename string.
186223
187-
Return the filename for the specified module.
224+
Return the filename for the specified module or raise ZipImportError
225+
if it couldn't be imported.
188226
"""
189227
# Deciding the filename requires working out where the code
190228
# would come from if the module was actually loaded
@@ -236,8 +274,13 @@ def load_module(self, fullname):
236274
237275
Load the module specified by 'fullname'. 'fullname' must be the
238276
fully qualified (dotted) module name. It returns the imported
239-
module, or raises ZipImportError if it wasn't found.
277+
module, or raises ZipImportError if it could not be imported.
278+
279+
Deprecated since Python 3.10. Use exec_module() instead.
240280
"""
281+
msg = ("zipimport.zipimporter.load_module() is deprecated and slated for "
282+
"removal in Python 3.12; use exec_module() instead")
283+
_warnings.warn(msg, DeprecationWarning)
241284
code, ispackage, modpath = _get_module_code(self, fullname)
242285
mod = sys.modules.get(fullname)
243286
if mod is None or not isinstance(mod, _module_type):
@@ -280,11 +323,18 @@ def get_resource_reader(self, fullname):
280323
return None
281324
except ZipImportError:
282325
return None
283-
if not _ZipImportResourceReader._registered:
284-
from importlib.abc import ResourceReader
285-
ResourceReader.register(_ZipImportResourceReader)
286-
_ZipImportResourceReader._registered = True
287-
return _ZipImportResourceReader(self, fullname)
326+
from importlib.readers import ZipReader
327+
return ZipReader(self, fullname)
328+
329+
330+
def invalidate_caches(self):
331+
"""Reload the file data of the archive path."""
332+
try:
333+
self._files = _read_directory(self.archive)
334+
_zip_directory_cache[self.archive] = self._files
335+
except ZipImportError:
336+
_zip_directory_cache.pop(self.archive, None)
337+
self._files = {}
288338

289339

290340
def __repr__(self):
@@ -580,20 +630,15 @@ def _eq_mtime(t1, t2):
580630

581631

582632
# Given the contents of a .py[co] file, unmarshal the data
583-
# and return the code object. Return None if it the magic word doesn't
584-
# match, or if the recorded .py[co] metadata does not match the source,
585-
# (we do this instead of raising an exception as we fall back
586-
# to .py if available and we don't want to mask other errors).
633+
# and return the code object. Raises ImportError it the magic word doesn't
634+
# match, or if the recorded .py[co] metadata does not match the source.
587635
def _unmarshal_code(self, pathname, fullpath, fullname, data):
588636
exc_details = {
589637
'name': fullname,
590638
'path': fullpath,
591639
}
592640

593-
try:
594-
flags = _bootstrap_external._classify_pyc(data, fullname, exc_details)
595-
except ImportError:
596-
return None
641+
flags = _bootstrap_external._classify_pyc(data, fullname, exc_details)
597642

598643
hash_based = flags & 0b1 != 0
599644
if hash_based:
@@ -607,11 +652,8 @@ def _unmarshal_code(self, pathname, fullpath, fullname, data):
607652
source_bytes,
608653
)
609654

610-
try:
611-
_bootstrap_external._validate_hash_pyc(
612-
data, source_hash, fullname, exc_details)
613-
except ImportError:
614-
return None
655+
_bootstrap_external._validate_hash_pyc(
656+
data, source_hash, fullname, exc_details)
615657
else:
616658
source_mtime, source_size = \
617659
_get_mtime_and_size_of_source(self, fullpath)
@@ -697,6 +739,7 @@ def _get_pyc_source(self, path):
697739
# 'fullname'.
698740
def _get_module_code(self, fullname):
699741
path = _get_module_path(self, fullname)
742+
import_error = None
700743
for suffix, isbytecode, ispackage in _zip_searchorder:
701744
fullpath = path + suffix
702745
_bootstrap._verbose_message('trying {}{}{}', self.archive, path_sep, fullpath, verbosity=2)
@@ -707,8 +750,12 @@ def _get_module_code(self, fullname):
707750
else:
708751
modpath = toc_entry[0]
709752
data = _get_data(self.archive, toc_entry)
753+
code = None
710754
if isbytecode:
711-
code = _unmarshal_code(self, modpath, fullpath, fullname, data)
755+
try:
756+
code = _unmarshal_code(self, modpath, fullpath, fullname, data)
757+
except ImportError as exc:
758+
import_error = exc
712759
else:
713760
code = _compile_source(modpath, data)
714761
if code is None:
@@ -718,75 +765,8 @@ def _get_module_code(self, fullname):
718765
modpath = toc_entry[0]
719766
return code, ispackage, modpath
720767
else:
721-
raise ZipImportError(f"can't find module {fullname!r}", name=fullname)
722-
723-
724-
class _ZipImportResourceReader:
725-
"""Private class used to support ZipImport.get_resource_reader().
726-
727-
This class is allowed to reference all the innards and private parts of
728-
the zipimporter.
729-
"""
730-
_registered = False
731-
732-
def __init__(self, zipimporter, fullname):
733-
self.zipimporter = zipimporter
734-
self.fullname = fullname
735-
736-
def open_resource(self, resource):
737-
fullname_as_path = self.fullname.replace('.', '/')
738-
path = f'{fullname_as_path}/{resource}'
739-
from io import BytesIO
740-
try:
741-
return BytesIO(self.zipimporter.get_data(path))
742-
except OSError:
743-
raise FileNotFoundError(path)
744-
745-
def resource_path(self, resource):
746-
# All resources are in the zip file, so there is no path to the file.
747-
# Raising FileNotFoundError tells the higher level API to extract the
748-
# binary data and create a temporary file.
749-
raise FileNotFoundError
750-
751-
def is_resource(self, name):
752-
# Maybe we could do better, but if we can get the data, it's a
753-
# resource. Otherwise it isn't.
754-
fullname_as_path = self.fullname.replace('.', '/')
755-
path = f'{fullname_as_path}/{name}'
756-
try:
757-
self.zipimporter.get_data(path)
758-
except OSError:
759-
return False
760-
return True
761-
762-
def contents(self):
763-
# This is a bit convoluted, because fullname will be a module path,
764-
# but _files is a list of file names relative to the top of the
765-
# archive's namespace. We want to compare file paths to find all the
766-
# names of things inside the module represented by fullname. So we
767-
# turn the module path of fullname into a file path relative to the
768-
# top of the archive, and then we iterate through _files looking for
769-
# names inside that "directory".
770-
from pathlib import Path
771-
fullname_path = Path(self.zipimporter.get_filename(self.fullname))
772-
relative_path = fullname_path.relative_to(self.zipimporter.archive)
773-
# Don't forget that fullname names a package, so its path will include
774-
# __init__.py, which we want to ignore.
775-
assert relative_path.name == '__init__.py'
776-
package_path = relative_path.parent
777-
subdirs_seen = set()
778-
for filename in self.zipimporter._files:
779-
try:
780-
relative = Path(filename).relative_to(package_path)
781-
except ValueError:
782-
continue
783-
# If the path of the file (which is relative to the top of the zip
784-
# namespace), relative to the package given when the resource
785-
# reader was created, has a parent, then it's a name in a
786-
# subdirectory and thus we skip it.
787-
parent_name = relative.parent.name
788-
if len(parent_name) == 0:
789-
yield relative.name
790-
elif parent_name not in subdirs_seen:
791-
subdirs_seen.add(parent_name)
792-
yield parent_name
768+
if import_error:
769+
msg = f"module load failed: {import_error}"
770+
raise ZipImportError(msg, name=fullname) from import_error
771+
else:
772+
raise ZipImportError(f"can't find module {fullname!r}", name=fullname)

0 commit comments

Comments
 (0)