From be65d3dc2cd3e156920f4ccfb8ec3eb34bcb3a74 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sun, 25 Feb 2024 12:33:33 -0500 Subject: [PATCH 1/7] Move compatibility and future behaviors into separate packages. --- importlib_resources/_common.py | 2 +- importlib_resources/abc.py | 3 +- importlib_resources/compat/__init__.py | 0 importlib_resources/compat/py38.py | 11 +++++++ importlib_resources/compat/py39.py | 10 ++++++ importlib_resources/future/__init__.py | 0 .../{_compat.py => future/adapters.py} | 31 +------------------ importlib_resources/readers.py | 2 +- 8 files changed, 26 insertions(+), 33 deletions(-) create mode 100644 importlib_resources/compat/__init__.py create mode 100644 importlib_resources/compat/py38.py create mode 100644 importlib_resources/compat/py39.py create mode 100644 importlib_resources/future/__init__.py rename importlib_resources/{_compat.py => future/adapters.py} (77%) diff --git a/importlib_resources/_common.py b/importlib_resources/_common.py index b591cce5..c9891359 100644 --- a/importlib_resources/_common.py +++ b/importlib_resources/_common.py @@ -12,7 +12,7 @@ from typing import Union, Optional, cast from .abc import ResourceReader, Traversable -from ._compat import wrap_spec +from .future.adapters import wrap_spec Package = Union[types.ModuleType, str] Anchor = Package diff --git a/importlib_resources/abc.py b/importlib_resources/abc.py index 23b6aeaf..7a58dd2f 100644 --- a/importlib_resources/abc.py +++ b/importlib_resources/abc.py @@ -3,8 +3,9 @@ import itertools import pathlib from typing import Any, BinaryIO, Iterable, Iterator, NoReturn, Text, Optional +from typing import runtime_checkable, Protocol -from ._compat import runtime_checkable, Protocol, StrPath +from .compat.py38 import StrPath __all__ = ["ResourceReader", "Traversable", "TraversableResources"] diff --git a/importlib_resources/compat/__init__.py b/importlib_resources/compat/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/importlib_resources/compat/py38.py b/importlib_resources/compat/py38.py new file mode 100644 index 00000000..4d548257 --- /dev/null +++ b/importlib_resources/compat/py38.py @@ -0,0 +1,11 @@ +import os +import sys + +from typing import Union + + +if sys.version_info >= (3, 9): + StrPath = Union[str, os.PathLike[str]] +else: + # PathLike is only subscriptable at runtime in 3.9+ + StrPath = Union[str, "os.PathLike[str]"] diff --git a/importlib_resources/compat/py39.py b/importlib_resources/compat/py39.py new file mode 100644 index 00000000..ab87b9dc --- /dev/null +++ b/importlib_resources/compat/py39.py @@ -0,0 +1,10 @@ +import sys + + +__all__ = ['ZipPath'] + + +if sys.version_info >= (3, 10): + from zipfile import Path as ZipPath # type: ignore +else: + from zipp import Path as ZipPath # type: ignore diff --git a/importlib_resources/future/__init__.py b/importlib_resources/future/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/importlib_resources/_compat.py b/importlib_resources/future/adapters.py similarity index 77% rename from importlib_resources/_compat.py rename to importlib_resources/future/adapters.py index 519ccfa4..1f0cad12 100644 --- a/importlib_resources/_compat.py +++ b/importlib_resources/future/adapters.py @@ -8,25 +8,7 @@ from contextlib import suppress from typing import Union - -if sys.version_info >= (3, 10): - from zipfile import Path as ZipPath # type: ignore -else: - from zipp import Path as ZipPath # type: ignore - - -try: - from typing import runtime_checkable # type: ignore -except ImportError: - - def runtime_checkable(cls): # type: ignore - return cls - - -try: - from typing import Protocol # type: ignore -except ImportError: - Protocol = abc.ABC # type: ignore +from .. import readers, _adapters class TraversableResourcesLoader: @@ -46,8 +28,6 @@ def path(self): return self.spec.origin def get_resource_reader(self, name): - from . import readers, _adapters - def _zip_reader(spec): with suppress(AttributeError): return readers.ZipReader(spec.loader, spec.name) @@ -98,13 +78,4 @@ def wrap_spec(package): Supersedes _adapters.wrap_spec to use TraversableResourcesLoader from above for older Python compatibility (<3.10). """ - from . import _adapters - return _adapters.SpecLoaderAdapter(package.__spec__, TraversableResourcesLoader) - - -if sys.version_info >= (3, 9): - StrPath = Union[str, os.PathLike[str]] -else: - # PathLike is only subscriptable at runtime in 3.9+ - StrPath = Union[str, "os.PathLike[str]"] diff --git a/importlib_resources/readers.py b/importlib_resources/readers.py index ef2d21ad..cad10e53 100644 --- a/importlib_resources/readers.py +++ b/importlib_resources/readers.py @@ -9,7 +9,7 @@ from . import abc from ._itertools import only -from ._compat import ZipPath +from .compat.py39 import ZipPath def remove_duplicates(items): From 8b10d7ecb697c3ceb666b22e1e066c968ec66f35 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sun, 25 Feb 2024 12:51:55 -0500 Subject: [PATCH 2/7] Consolidate more shared behavior across TraversableResourceLoaders. --- importlib_resources/future/adapters.py | 20 +++----------------- 1 file changed, 3 insertions(+), 17 deletions(-) diff --git a/importlib_resources/future/adapters.py b/importlib_resources/future/adapters.py index 1f0cad12..cd5067dc 100644 --- a/importlib_resources/future/adapters.py +++ b/importlib_resources/future/adapters.py @@ -11,7 +11,7 @@ from .. import readers, _adapters -class TraversableResourcesLoader: +class TraversableResourcesLoader(_adapters.TraversableResourcesLoader): """ Adapt loaders to provide TraversableResources and other compatibility. @@ -20,9 +20,6 @@ class TraversableResourcesLoader: loaders do not yet implement TraversableResources. """ - def __init__(self, spec): - self.spec = spec - @property def path(self): return self.spec.origin @@ -36,14 +33,6 @@ def _namespace_reader(spec): with suppress(AttributeError, ValueError): return readers.NamespaceReader(spec.submodule_search_locations) - def _available_reader(spec): - with suppress(AttributeError): - return spec.loader.get_resource_reader(spec.name) - - def _native_reader(spec): - reader = _available_reader(spec) - return reader if hasattr(reader, 'files') else None - def _file_reader(spec): try: path = pathlib.Path(self.path) @@ -62,11 +51,8 @@ def _file_reader(spec): # local FileReader _file_reader(self.spec) or - # native reader if it supplies 'files' - _native_reader(self.spec) - or - # fallback - adapt the spec ResourceReader to TraversableReader - _adapters.CompatibilityFiles(self.spec) + # fallback + super().get_resource_reader(name) ) From beb50118fdc76e396deb0b6ca1d30a3339844979 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sun, 25 Feb 2024 13:00:08 -0500 Subject: [PATCH 3/7] Update comments to reflect refreshed purpose. --- importlib_resources/future/adapters.py | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/importlib_resources/future/adapters.py b/importlib_resources/future/adapters.py index cd5067dc..d787f1b4 100644 --- a/importlib_resources/future/adapters.py +++ b/importlib_resources/future/adapters.py @@ -16,8 +16,8 @@ class TraversableResourcesLoader(_adapters.TraversableResourcesLoader): Adapt loaders to provide TraversableResources and other compatibility. - Used primarily for Python 3.9 and earlier where the native - loaders do not yet implement TraversableResources. + Ensures the readers from importlib_resources are preferred + over stdlib readers. """ @property @@ -58,10 +58,8 @@ def _file_reader(spec): def wrap_spec(package): """ - Construct a package spec with traversable compatibility - on the spec/loader/reader. - - Supersedes _adapters.wrap_spec to use TraversableResourcesLoader - from above for older Python compatibility (<3.10). + Override _adapters.wrap_spec to use TraversableResourcesLoader + from above. Ensures that future behavior is always available on older + Pythons. """ return _adapters.SpecLoaderAdapter(package.__spec__, TraversableResourcesLoader) From 115ca5dee0bdecff43766be789f34979efb8d788 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sun, 25 Feb 2024 13:22:04 -0500 Subject: [PATCH 4/7] Move reader constructors into methods. --- importlib_resources/future/adapters.py | 46 +++++++++++++------------- 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/importlib_resources/future/adapters.py b/importlib_resources/future/adapters.py index d787f1b4..1983edb2 100644 --- a/importlib_resources/future/adapters.py +++ b/importlib_resources/future/adapters.py @@ -20,41 +20,41 @@ class TraversableResourcesLoader(_adapters.TraversableResourcesLoader): over stdlib readers. """ - @property - def path(self): - return self.spec.origin - def get_resource_reader(self, name): - def _zip_reader(spec): - with suppress(AttributeError): - return readers.ZipReader(spec.loader, spec.name) - - def _namespace_reader(spec): - with suppress(AttributeError, ValueError): - return readers.NamespaceReader(spec.submodule_search_locations) - - def _file_reader(spec): - try: - path = pathlib.Path(self.path) - except TypeError: - return None - if path.exists(): - return readers.FileReader(self) - return ( # local ZipReader if a zip module - _zip_reader(self.spec) + self._zip_reader() or # local NamespaceReader if a namespace module - _namespace_reader(self.spec) + self._namespace_reader() or # local FileReader - _file_reader(self.spec) + self._file_reader() or # fallback super().get_resource_reader(name) ) + @property + def path(self): + return self.spec.origin + + def _zip_reader(self): + with suppress(AttributeError): + return readers.ZipReader(self.spec.loader, self.spec.name) + + def _namespace_reader(self): + with suppress(AttributeError, ValueError): + return readers.NamespaceReader(self.spec.submodule_search_locations) + + def _file_reader(self): + try: + path = pathlib.Path(self.path) + except TypeError: + return None + if path.exists(): + return readers.FileReader(self) + def wrap_spec(package): """ From 321797927c8460c1e9432863a4244cbb7285a908 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sun, 25 Feb 2024 13:23:26 -0500 Subject: [PATCH 5/7] Use a SimpleNamespace to pass the path needed by the FileReader. --- importlib_resources/future/adapters.py | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/importlib_resources/future/adapters.py b/importlib_resources/future/adapters.py index 1983edb2..63e3b991 100644 --- a/importlib_resources/future/adapters.py +++ b/importlib_resources/future/adapters.py @@ -6,6 +6,7 @@ import pathlib import warnings from contextlib import suppress +from types import SimpleNamespace from typing import Union from .. import readers, _adapters @@ -35,10 +36,6 @@ def get_resource_reader(self, name): super().get_resource_reader(name) ) - @property - def path(self): - return self.spec.origin - def _zip_reader(self): with suppress(AttributeError): return readers.ZipReader(self.spec.loader, self.spec.name) @@ -49,11 +46,11 @@ def _namespace_reader(self): def _file_reader(self): try: - path = pathlib.Path(self.path) + path = pathlib.Path(self.spec.origin) except TypeError: return None if path.exists(): - return readers.FileReader(self) + return readers.FileReader(SimpleNamespace(path=path)) def wrap_spec(package): From 12c3c6296f00610d7ecd69fb633719eafb85858f Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sun, 25 Feb 2024 13:27:04 -0500 Subject: [PATCH 6/7] Extract method for the standard readers. --- importlib_resources/future/adapters.py | 17 ++++------------- 1 file changed, 4 insertions(+), 13 deletions(-) diff --git a/importlib_resources/future/adapters.py b/importlib_resources/future/adapters.py index 63e3b991..2d67c35c 100644 --- a/importlib_resources/future/adapters.py +++ b/importlib_resources/future/adapters.py @@ -22,19 +22,10 @@ class TraversableResourcesLoader(_adapters.TraversableResourcesLoader): """ def get_resource_reader(self, name): - return ( - # local ZipReader if a zip module - self._zip_reader() - or - # local NamespaceReader if a namespace module - self._namespace_reader() - or - # local FileReader - self._file_reader() - or - # fallback - super().get_resource_reader(name) - ) + return self._standard_reader() or super().get_resource_reader(name) + + def _standard_reader(self): + return self._zip_reader() or self._namespace_reader() or self._file_reader() def _zip_reader(self): with suppress(AttributeError): From 37368450def57ab7877e815a30f58869a04ee36f Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sun, 25 Feb 2024 13:28:04 -0500 Subject: [PATCH 7/7] Remove flake8 exclusion and remove unused imports. --- importlib_resources/future/adapters.py | 7 ------- 1 file changed, 7 deletions(-) diff --git a/importlib_resources/future/adapters.py b/importlib_resources/future/adapters.py index 2d67c35c..56dea7cf 100644 --- a/importlib_resources/future/adapters.py +++ b/importlib_resources/future/adapters.py @@ -1,13 +1,6 @@ -# flake8: noqa - -import abc -import os -import sys import pathlib -import warnings from contextlib import suppress from types import SimpleNamespace -from typing import Union from .. import readers, _adapters