Format: text/x-python
⬇ Download File (2.6 KB)
   1  """A shim module for deprecated imports
   2  """
   3  # Copyright (c) IPython Development Team.
   4  # Distributed under the terms of the Modified BSD License.
   5  
   6  import importlib.abc
   7  import importlib.util
   8  import sys
   9  import types
  10  from importlib import import_module
  11  
  12  from .importstring import import_item
  13  
  14  
  15  class ShimWarning(Warning):
  16      """A warning to show when a module has moved, and a shim is in its place."""
  17  
  18  
  19  class ShimImporter(importlib.abc.MetaPathFinder):
  20      """Import hook for a shim.
  21  
  22      This ensures that submodule imports return the real target module,
  23      not a clone that will confuse `is` and `isinstance` checks.
  24      """
  25      def __init__(self, src, mirror):
  26          self.src = src
  27          self.mirror = mirror
  28  
  29      def _mirror_name(self, fullname):
  30          """get the name of the mirrored module"""
  31  
  32          return self.mirror + fullname[len(self.src) :]
  33  
  34      def find_spec(self, fullname, path, target=None):
  35          if fullname.startswith(self.src + "."):
  36              mirror_name = self._mirror_name(fullname)
  37              return importlib.util.find_spec(mirror_name)
  38  
  39  
  40  class ShimModule(types.ModuleType):
  41  
  42      def __init__(self, *args, **kwargs):
  43          self._mirror = kwargs.pop("mirror")
  44          src = kwargs.pop("src", None)
  45          if src:
  46              kwargs['name'] = src.rsplit('.', 1)[-1]
  47          super(ShimModule, self).__init__(*args, **kwargs)
  48          # add import hook for descendent modules
  49          if src:
  50              sys.meta_path.append(
  51                  ShimImporter(src=src, mirror=self._mirror)
  52              )
  53      
  54      @property
  55      def __path__(self):
  56          return []
  57      
  58      @property
  59      def __spec__(self):
  60          """Don't produce __spec__ until requested"""
  61          return import_module(self._mirror).__spec__
  62      
  63      def __dir__(self):
  64          return dir(import_module(self._mirror))
  65      
  66      @property
  67      def __all__(self):
  68          """Ensure __all__ is always defined"""
  69          mod = import_module(self._mirror)
  70          try:
  71              return mod.__all__
  72          except AttributeError:
  73              return [name for name in dir(mod) if not name.startswith('_')]
  74  
  75      def __getattr__(self, key):
  76          # Use the equivalent of import_item(name), see below
  77          name = "%s.%s" % (self._mirror, key)
  78          try:
  79              return import_item(name)
  80          except ImportError as e:
  81              raise AttributeError(key) from e
  82  
  83      def __repr__(self):
  84          # repr on a module can be called during error handling; make sure
  85          # it does not fail, even if the import fails
  86          try:
  87              return self.__getattr__("__repr__")()
  88          except AttributeError:
  89              return f"<ShimModule for {self._mirror!r}>"
  90