diff --git a/docs/changes.rst b/docs/changes.rst index 06946e60..fd519c5f 100644 --- a/docs/changes.rst +++ b/docs/changes.rst @@ -18,6 +18,10 @@ Version 1.14.0 implemented such that you don't have to use `Type.__wrapped__` instead of `Type` as last argument to `isinstance()`. +* Eliminated deprecation warnings related to Python module import system, which + would have turned into broken code in Python 3.12. This was used by the post + import hook mechanism. + **New Features** * Binary wheels provided on PyPi for `aarch64` Linux systems and macOS diff --git a/src/wrapt/importer.py b/src/wrapt/importer.py index 2c9db6fb..755029c1 100644 --- a/src/wrapt/importer.py +++ b/src/wrapt/importer.py @@ -10,9 +10,10 @@ if PY2: string_types = basestring, + find_spec = None else: - import importlib string_types = str, + from importlib.util import find_spec from .decorators import synchronized @@ -158,6 +159,16 @@ def load_module(self, fullname): return module + # Python 3.4 introduced create_module() and exec_module() instead of + # load_module() alone. Splitting the two steps. + + def create_module(self, spec): + return self.loader.create_module(spec) + + def exec_module(self, module): + self.loader.exec_module(module) + notify_module_loaded(module) + class ImportHookFinder: def __init__(self): @@ -187,7 +198,7 @@ def find_module(self, fullname, path=None): # Now call back into the import system again. try: - if PY2: + if not find_spec: # For Python 2 we don't have much choice but to # call back in to __import__(). This will # actually cause the module to be imported. If no @@ -208,14 +219,52 @@ def find_module(self, fullname, path=None): # our own loader which will then in turn call the # real loader to import the module and invoke the # post import hooks. - try: - import importlib.util - loader = importlib.util.find_spec(fullname).loader - except (ImportError, AttributeError): - loader = importlib.find_loader(fullname, path) - if loader: + + loader = getattr(find_spec(fullname), "loader", None) + + if loader and not isinstance(loader, _ImportHookChainedLoader): return _ImportHookChainedLoader(loader) + finally: + del self.in_progress[fullname] + + def find_spec(self, fullname, path=None, target=None): + # Since Python 3.4, you are meant to implement find_spec() method + # instead of find_module() and since Python 3.10 you get deprecation + # warnings if you don't define find_spec(). + + # If the module being imported is not one we have registered + # post import hooks for, we can return immediately. We will + # take no further part in the importing of this module. + + if not fullname in _post_import_hooks: + return None + + # When we are interested in a specific module, we will call back + # into the import system a second time to defer to the import + # finder that is supposed to handle the importing of the module. + # We set an in progress flag for the target module so that on + # the second time through we don't trigger another call back + # into the import system and cause a infinite loop. + + if fullname in self.in_progress: + return None + + self.in_progress[fullname] = True + + # Now call back into the import system again. + + try: + # This should only be Python 3 so find_spec() should always + # exist so don't need to check. + + spec = find_spec(fullname) + loader = getattr(spec, "loader", None) + + if loader and not isinstance(loader, _ImportHookChainedLoader): + spec.loader = _ImportHookChainedLoader(loader) + + return spec finally: del self.in_progress[fullname]