diff options
author | Jason R. Coombs <jaraco@jaraco.com> | 2020-06-08 01:00:51 (GMT) |
---|---|---|
committer | GitHub <noreply@github.com> | 2020-06-08 01:00:51 (GMT) |
commit | 843c27765652e2322011fb3e5d88f4837de38c06 (patch) | |
tree | 84c801b6d0c7026c4623389daf390faa310182c5 /Lib/importlib/_common.py | |
parent | 972ab0327675e695373fc6272d5ac24e187579ad (diff) | |
download | cpython-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.py | 82 |
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 |