shimmodule.py 2.6 KB

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