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/resources.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/resources.py')
-rw-r--r-- | Lib/importlib/resources.py | 85 |
1 files changed, 14 insertions, 71 deletions
diff --git a/Lib/importlib/resources.py b/Lib/importlib/resources.py index b803a01..4535619 100644 --- a/Lib/importlib/resources.py +++ b/Lib/importlib/resources.py @@ -1,15 +1,13 @@ import os -from . import abc as resources_abc from . import _common -from ._common import as_file +from ._common import as_file, files from contextlib import contextmanager, suppress -from importlib import import_module from importlib.abc import ResourceLoader from io import BytesIO, TextIOWrapper from pathlib import Path from types import ModuleType -from typing import ContextManager, Iterable, Optional, Union +from typing import ContextManager, Iterable, Union from typing import cast from typing.io import BinaryIO, TextIO @@ -33,60 +31,11 @@ Package = Union[str, ModuleType] Resource = Union[str, os.PathLike] -def _resolve(name) -> ModuleType: - """If name is a string, resolve to a module.""" - if hasattr(name, '__spec__'): - return name - return import_module(name) - - -def _get_package(package) -> ModuleType: - """Take a package name or module object and return the module. - - If a name, the module is imported. If the resolved module - object is not a package, raise an exception. - """ - module = _resolve(package) - if module.__spec__.submodule_search_locations is None: - raise TypeError('{!r} is not a package'.format(package)) - return module - - -def _normalize_path(path) -> str: - """Normalize a path by ensuring it is a string. - - If the resulting string contains path separators, an exception is raised. - """ - parent, file_name = os.path.split(path) - if parent: - raise ValueError('{!r} must be only a file name'.format(path)) - return file_name - - -def _get_resource_reader( - package: ModuleType) -> Optional[resources_abc.ResourceReader]: - # Return the package's loader if it's a ResourceReader. 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__ - if hasattr(spec.loader, 'get_resource_reader'): - return cast(resources_abc.ResourceReader, - spec.loader.get_resource_reader(spec.name)) - return None - - -def _check_location(package): - if package.__spec__.origin is None or not package.__spec__.has_location: - raise FileNotFoundError(f'Package has no location {package!r}') - - def open_binary(package: Package, resource: Resource) -> BinaryIO: """Return a file-like object opened for binary reading of the resource.""" - resource = _normalize_path(resource) - package = _get_package(package) - reader = _get_resource_reader(package) + resource = _common.normalize_path(resource) + package = _common.get_package(package) + reader = _common.get_resource_reader(package) if reader is not None: return reader.open_resource(resource) absolute_package_path = os.path.abspath( @@ -140,13 +89,6 @@ def read_text(package: Package, return fp.read() -def files(package: Package) -> resources_abc.Traversable: - """ - Get a Traversable resource from a package - """ - return _common.from_package(_get_package(package)) - - def path( package: Package, resource: Resource, ) -> 'ContextManager[Path]': @@ -158,17 +100,18 @@ def path( raised if the file was deleted prior to the context manager exiting). """ - reader = _get_resource_reader(_get_package(package)) + reader = _common.get_resource_reader(_common.get_package(package)) return ( _path_from_reader(reader, resource) if reader else - _common.as_file(files(package).joinpath(_normalize_path(resource))) + _common.as_file( + _common.files(package).joinpath(_common.normalize_path(resource))) ) @contextmanager def _path_from_reader(reader, resource): - norm_resource = _normalize_path(resource) + norm_resource = _common.normalize_path(resource) with suppress(FileNotFoundError): yield Path(reader.resource_path(norm_resource)) return @@ -182,9 +125,9 @@ def is_resource(package: Package, name: str) -> bool: Directories are *not* resources. """ - package = _get_package(package) - _normalize_path(name) - reader = _get_resource_reader(package) + package = _common.get_package(package) + _common.normalize_path(name) + reader = _common.get_resource_reader(package) if reader is not None: return reader.is_resource(name) package_contents = set(contents(package)) @@ -200,8 +143,8 @@ def contents(package: Package) -> Iterable[str]: not considered resources. Use `is_resource()` on each entry returned here to check if it is a resource or not. """ - package = _get_package(package) - reader = _get_resource_reader(package) + package = _common.get_package(package) + reader = _common.get_resource_reader(package) if reader is not None: return reader.contents() # Is the package a namespace package? By definition, namespace packages |