summaryrefslogtreecommitdiffstats
path: root/Lib/zipimport.py
diff options
context:
space:
mode:
authorSerhiy Storchaka <storchaka@gmail.com>2018-09-19 06:28:06 (GMT)
committerGitHub <noreply@github.com>2018-09-19 06:28:06 (GMT)
commit9da3961f364da2a3ced740230b85ffb4309238d3 (patch)
tree3bdba1d723f7e24e6c27139b398a4c2866974ff4 /Lib/zipimport.py
parent2a9c3805ddedf282881ef7811a561c70b74f80b1 (diff)
downloadcpython-9da3961f364da2a3ced740230b85ffb4309238d3.zip
cpython-9da3961f364da2a3ced740230b85ffb4309238d3.tar.gz
cpython-9da3961f364da2a3ced740230b85ffb4309238d3.tar.bz2
bpo-25711: Move _ZipImportResourceReader from importlib to zipimport. (GH-9406)
Diffstat (limited to 'Lib/zipimport.py')
-rw-r--r--Lib/zipimport.py83
1 files changed, 81 insertions, 2 deletions
diff --git a/Lib/zipimport.py b/Lib/zipimport.py
index 059f124..4017340 100644
--- a/Lib/zipimport.py
+++ b/Lib/zipimport.py
@@ -272,8 +272,16 @@ class zipimporter:
If 'fullname' is a package within the zip file, return the
'ResourceReader' object for the package. Otherwise return None.
"""
- from importlib import resources
- return resources._zipimport_get_resource_reader(self, fullname)
+ try:
+ if not self.is_package(fullname):
+ return None
+ except ZipImportError:
+ return None
+ if not _ZipImportResourceReader._registered:
+ from importlib.abc import ResourceReader
+ ResourceReader.register(_ZipImportResourceReader)
+ _ZipImportResourceReader._registered = True
+ return _ZipImportResourceReader(self, fullname)
def __repr__(self):
@@ -648,3 +656,74 @@ def _get_module_code(self, fullname):
return code, ispackage, modpath
else:
raise ZipImportError(f"can't find module {fullname!r}", name=fullname)
+
+
+class _ZipImportResourceReader:
+ """Private class used to support ZipImport.get_resource_reader().
+
+ This class is allowed to reference all the innards and private parts of
+ the zipimporter.
+ """
+ _registered = False
+
+ def __init__(self, zipimporter, fullname):
+ self.zipimporter = zipimporter
+ self.fullname = fullname
+
+ def open_resource(self, resource):
+ fullname_as_path = self.fullname.replace('.', '/')
+ path = f'{fullname_as_path}/{resource}'
+ from io import BytesIO
+ try:
+ return BytesIO(self.zipimporter.get_data(path))
+ except OSError:
+ raise FileNotFoundError(path)
+
+ def resource_path(self, resource):
+ # All resources are in the zip file, so there is no path to the file.
+ # Raising FileNotFoundError tells the higher level API to extract the
+ # binary data and create a temporary file.
+ raise FileNotFoundError
+
+ def is_resource(self, name):
+ # Maybe we could do better, but if we can get the data, it's a
+ # resource. Otherwise it isn't.
+ fullname_as_path = self.fullname.replace('.', '/')
+ path = f'{fullname_as_path}/{name}'
+ try:
+ self.zipimporter.get_data(path)
+ except OSError:
+ return False
+ return True
+
+ def contents(self):
+ # This is a bit convoluted, because fullname will be a module path,
+ # but _files is a list of file names relative to the top of the
+ # archive's namespace. We want to compare file paths to find all the
+ # names of things inside the module represented by fullname. So we
+ # turn the module path of fullname into a file path relative to the
+ # top of the archive, and then we iterate through _files looking for
+ # names inside that "directory".
+ from pathlib import Path
+ fullname_path = Path(self.zipimporter.get_filename(self.fullname))
+ relative_path = fullname_path.relative_to(self.zipimporter.archive)
+ # Don't forget that fullname names a package, so its path will include
+ # __init__.py, which we want to ignore.
+ assert relative_path.name == '__init__.py'
+ package_path = relative_path.parent
+ subdirs_seen = set()
+ for filename in self.zipimporter._files:
+ try:
+ relative = Path(filename).relative_to(package_path)
+ except ValueError:
+ continue
+ # If the path of the file (which is relative to the top of the zip
+ # namespace), relative to the package given when the resource
+ # reader was created, has a parent, then it's a name in a
+ # subdirectory and thus we skip it.
+ parent_name = relative.parent.name
+ if len(parent_name) == 0:
+ yield relative.name
+ elif parent_name not in subdirs_seen:
+ subdirs_seen.add(parent_name)
+ yield parent_name