From 527173b872b7673a0d43fa042dab9f095b24181f Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sun, 25 Feb 2024 14:16:46 -0500 Subject: [PATCH 1/3] Give precedence to native traversable readers. Ensure that standard library readers are replaced while third-party readers are passed along. Closes #295. --- importlib_resources/future/adapters.py | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/importlib_resources/future/adapters.py b/importlib_resources/future/adapters.py index 56dea7c..3c2d0fb 100644 --- a/importlib_resources/future/adapters.py +++ b/importlib_resources/future/adapters.py @@ -1,3 +1,4 @@ +import contextlib import pathlib from contextlib import suppress from types import SimpleNamespace @@ -15,7 +16,18 @@ class TraversableResourcesLoader(_adapters.TraversableResourcesLoader): """ def get_resource_reader(self, name): - return self._standard_reader() or super().get_resource_reader(name) + with contextlib.suppress(Exception): + return self._block_standard(super().get_resource_reader(name)) + return self._standard_reader() + + def _block_standard(self, reader): + """ + If the reader is from the standard library, raise an exception to + allow likely newer implementations in this library to take precedence. + """ + if reader.__class__.__module__.startswith('importlib.resources.'): + raise RuntimeError("Reader blocked to be superseded.") + return reader def _standard_reader(self): return self._zip_reader() or self._namespace_reader() or self._file_reader() From a7b1b802a29d3fa35f59162dd4a085cd38513d6e Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Wed, 6 Mar 2024 17:49:53 -0500 Subject: [PATCH 2/3] Re-implement _block_standard as a wrapper function around _adapters.TraversableResourcesLoader and ensure that the known standard readers are replaced by readers from importlib_resources. --- importlib_resources/future/adapters.py | 51 +++++++++++++++++++------- 1 file changed, 38 insertions(+), 13 deletions(-) diff --git a/importlib_resources/future/adapters.py b/importlib_resources/future/adapters.py index 3c2d0fb..9aa5b5e 100644 --- a/importlib_resources/future/adapters.py +++ b/importlib_resources/future/adapters.py @@ -1,4 +1,4 @@ -import contextlib +import functools import pathlib from contextlib import suppress from types import SimpleNamespace @@ -6,6 +6,39 @@ from .. import readers, _adapters +def _block_standard(reader_getter): + """ + Wrap _adapters.TraversableResourcesLoader.get_resource_reader + and intercept any standard library readers. + """ + + @functools.wraps(reader_getter) + def wrapper(*args, **kwargs): + """ + If the reader is from the standard library, return None to allow + allow likely newer implementations in this library to take precedence. + """ + try: + reader = reader_getter(*args, **kwargs) + except NotADirectoryError: + # MultiplexedPath may fail on zip subdirectory + return + # Python 3.10+ + if reader.__class__.__module__.startswith('importlib.resources.'): + return + # Python 3.8, 3.9 + if isinstance(reader, _adapters.CompatibilityFiles) and ( + reader.spec.loader.__class__.__module__.startswith('zipimport') + or reader.spec.loader.__class__.__module__.startswith( + '_frozen_importlib_external' + ) + ): + return + return reader + + return wrapper + + class TraversableResourcesLoader(_adapters.TraversableResourcesLoader): """ Adapt loaders to provide TraversableResources and other @@ -16,18 +49,10 @@ class TraversableResourcesLoader(_adapters.TraversableResourcesLoader): """ def get_resource_reader(self, name): - with contextlib.suppress(Exception): - return self._block_standard(super().get_resource_reader(name)) - return self._standard_reader() - - def _block_standard(self, reader): - """ - If the reader is from the standard library, raise an exception to - allow likely newer implementations in this library to take precedence. - """ - if reader.__class__.__module__.startswith('importlib.resources.'): - raise RuntimeError("Reader blocked to be superseded.") - return reader + return ( + _block_standard(super().get_resource_reader)(name) + or self._standard_reader() + ) def _standard_reader(self): return self._zip_reader() or self._namespace_reader() or self._file_reader() From 6d48bc93d76431b5538250d236c1c54c53f0fdcd Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Wed, 6 Mar 2024 17:54:26 -0500 Subject: [PATCH 3/3] Add news fragment. --- newsfragments/295.feature.rst | 1 + 1 file changed, 1 insertion(+) create mode 100644 newsfragments/295.feature.rst diff --git a/newsfragments/295.feature.rst b/newsfragments/295.feature.rst new file mode 100644 index 0000000..3d74253 --- /dev/null +++ b/newsfragments/295.feature.rst @@ -0,0 +1 @@ +Future compatibility adapters now ensure that standard library readers are replaced without overriding non-standard readers.