summaryrefslogtreecommitdiffstats
path: root/Lib
diff options
context:
space:
mode:
Diffstat (limited to 'Lib')
-rw-r--r--Lib/importlib/resources.py84
-rw-r--r--Lib/zipimport.py83
2 files changed, 81 insertions, 86 deletions
diff --git a/Lib/importlib/resources.py b/Lib/importlib/resources.py
index cbefdd5..fc3a1c9 100644
--- a/Lib/importlib/resources.py
+++ b/Lib/importlib/resources.py
@@ -257,87 +257,3 @@ def contents(package: Package) -> Iterable[str]:
else:
package_directory = Path(package.__spec__.origin).parent
return os.listdir(package_directory)
-
-
-# Private implementation of ResourceReader and get_resource_reader() called
-# from zipimport.c. Don't use these directly! We're implementing these in
-# Python because 1) it's easier, 2) zipimport may get rewritten in Python
-# itself at some point, so doing this all in C would difficult and a waste of
-# effort.
-
-class _ZipImportResourceReader(resources_abc.ResourceReader):
- """Private class used to support ZipImport.get_resource_reader().
-
- This class is allowed to reference all the innards and private parts of
- the zipimporter.
- """
-
- 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}'
- 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".
- 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
-
-
-# Called from zipimport.c
-def _zipimport_get_resource_reader(zipimporter, fullname):
- try:
- if not zipimporter.is_package(fullname):
- return None
- except ZipImportError:
- return None
- return _ZipImportResourceReader(zipimporter, fullname)
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