summaryrefslogtreecommitdiffstats
path: root/Lib/importlib/_common.py
diff options
context:
space:
mode:
authorJason R. Coombs <jaraco@jaraco.com>2020-06-08 01:00:51 (GMT)
committerGitHub <noreply@github.com>2020-06-08 01:00:51 (GMT)
commit843c27765652e2322011fb3e5d88f4837de38c06 (patch)
tree84c801b6d0c7026c4623389daf390faa310182c5 /Lib/importlib/_common.py
parent972ab0327675e695373fc6272d5ac24e187579ad (diff)
downloadcpython-843c27765652e2322011fb3e5d88f4837de38c06.zip
cpython-843c27765652e2322011fb3e5d88f4837de38c06.tar.gz
cpython-843c27765652e2322011fb3e5d88f4837de38c06.tar.bz2
bpo-39791 native hooks for importlib.resources.files (GH-20576)
* Provide native .files support on SourceFileLoader. * Add native importlib.resources.files() support to zipimporter. Remove fallback support. * make regen-all * 📜🤖 Added by blurb_it. * Move 'files' into the ResourceReader so it can carry the relevant module name context. * Create 'importlib.readers' module and add FileReader to it. * Add zip reader and rely on it for a TraversableResources object on zipimporter. * Remove TraversableAdapter, no longer needed. * Update blurb. * Replace backslashes with forward slashes. * Incorporate changes from importlib_metadata 2.0, finalizing the interface for extension via get_resource_reader. Co-authored-by: blurb-it[bot] <43283697+blurb-it[bot]@users.noreply.github.com>
Diffstat (limited to 'Lib/importlib/_common.py')
-rw-r--r--Lib/importlib/_common.py82
1 files changed, 63 insertions, 19 deletions
diff --git a/Lib/importlib/_common.py b/Lib/importlib/_common.py
index ba7cbac..b15c59e 100644
--- a/Lib/importlib/_common.py
+++ b/Lib/importlib/_common.py
@@ -1,38 +1,82 @@
import os
import pathlib
-import zipfile
import tempfile
import functools
import contextlib
+import types
+import importlib
+from typing import Union, Any, Optional
+from .abc import ResourceReader
-def from_package(package):
+Package = Union[types.ModuleType, str]
+
+
+def files(package):
"""
- Return a Traversable object for the given package.
+ Get a Traversable resource from a package
+ """
+ return from_package(get_package(package))
+
+def normalize_path(path):
+ # type: (Any) -> str
+ """Normalize a path by ensuring it is a string.
+
+ If the resulting string contains path separators, an exception is raised.
"""
- spec = package.__spec__
- return from_traversable_resources(spec) or fallback_resources(spec)
+ str_path = str(path)
+ parent, file_name = os.path.split(str_path)
+ if parent:
+ raise ValueError('{!r} must be only a file name'.format(path))
+ return file_name
-def from_traversable_resources(spec):
+def get_resource_reader(package):
+ # type: (types.ModuleType) -> Optional[ResourceReader]
"""
- If the spec.loader implements TraversableResources,
- directly or implicitly, it will have a ``files()`` method.
+ Return the package's loader if it's a ResourceReader.
"""
- with contextlib.suppress(AttributeError):
- return spec.loader.files()
+ # We can't use
+ # a issubclass() check here because apparently abc.'s __subclasscheck__()
+ # hook wants to create a weak reference to the object, but
+ # zipimport.zipimporter does not support weak references, resulting in a
+ # TypeError. That seems terrible.
+ spec = package.__spec__
+ reader = getattr(spec.loader, 'get_resource_reader', None)
+ if reader is None:
+ return None
+ return reader(spec.name)
-def fallback_resources(spec):
- package_directory = pathlib.Path(spec.origin).parent
- try:
- archive_path = spec.loader.archive
- rel_path = package_directory.relative_to(archive_path)
- return zipfile.Path(archive_path, str(rel_path) + '/')
- except Exception:
- pass
- return package_directory
+def resolve(cand):
+ # type: (Package) -> types.ModuleType
+ return (
+ cand if isinstance(cand, types.ModuleType)
+ else importlib.import_module(cand)
+ )
+
+
+def get_package(package):
+ # type: (Package) -> types.ModuleType
+ """Take a package name or module object and return the module.
+
+ Raise an exception if the resolved module is not a package.
+ """
+ resolved = resolve(package)
+ if resolved.__spec__.submodule_search_locations is None:
+ raise TypeError('{!r} is not a package'.format(package))
+ return resolved
+
+
+def from_package(package):
+ """
+ Return a Traversable object for the given package.
+
+ """
+ spec = package.__spec__
+ reader = spec.loader.get_resource_reader(spec.name)
+ return reader.files()
@contextlib.contextmanager