##// END OF EJS Templates
Merge pull request #13473 from jochen-ott-by/shimmodule-repr-fallback...
Matthias Bussonnier -
r27456:9057fd4f merge
parent child Browse files
Show More
@@ -0,0 +1,12 b''
1 from IPython.utils.shimmodule import ShimModule
2 import IPython
3
4
5 def test_shimmodule_repr_does_not_fail_on_import_error():
6 shim_module = ShimModule("shim_module", mirror="mirrored_module_does_not_exist")
7 repr(shim_module)
8
9
10 def test_shimmodule_repr_forwards_to_module():
11 shim_module = ShimModule("shim_module", mirror="IPython")
12 assert repr(shim_module) == repr(IPython)
@@ -1,81 +1,89 b''
1 1 """A shim module for deprecated imports
2 2 """
3 3 # Copyright (c) IPython Development Team.
4 4 # Distributed under the terms of the Modified BSD License.
5 5
6 6 import importlib.abc
7 7 import importlib.util
8 8 import sys
9 9 import types
10 10 from importlib import import_module
11 11
12 12 from .importstring import import_item
13 13
14 14
15 15 class ShimWarning(Warning):
16 16 """A warning to show when a module has moved, and a shim is in its place."""
17 17
18 18
19 19 class ShimImporter(importlib.abc.MetaPathFinder):
20 20 """Import hook for a shim.
21 21
22 22 This ensures that submodule imports return the real target module,
23 23 not a clone that will confuse `is` and `isinstance` checks.
24 24 """
25 25 def __init__(self, src, mirror):
26 26 self.src = src
27 27 self.mirror = mirror
28 28
29 29 def _mirror_name(self, fullname):
30 30 """get the name of the mirrored module"""
31 31
32 32 return self.mirror + fullname[len(self.src) :]
33 33
34 34 def find_spec(self, fullname, path, target=None):
35 35 if fullname.startswith(self.src + "."):
36 36 mirror_name = self._mirror_name(fullname)
37 37 return importlib.util.find_spec(mirror_name)
38 38
39 39
40 40 class ShimModule(types.ModuleType):
41 41
42 42 def __init__(self, *args, **kwargs):
43 43 self._mirror = kwargs.pop("mirror")
44 44 src = kwargs.pop("src", None)
45 45 if src:
46 46 kwargs['name'] = src.rsplit('.', 1)[-1]
47 47 super(ShimModule, self).__init__(*args, **kwargs)
48 48 # add import hook for descendent modules
49 49 if src:
50 50 sys.meta_path.append(
51 51 ShimImporter(src=src, mirror=self._mirror)
52 52 )
53 53
54 54 @property
55 55 def __path__(self):
56 56 return []
57 57
58 58 @property
59 59 def __spec__(self):
60 60 """Don't produce __spec__ until requested"""
61 61 return import_module(self._mirror).__spec__
62 62
63 63 def __dir__(self):
64 64 return dir(import_module(self._mirror))
65 65
66 66 @property
67 67 def __all__(self):
68 68 """Ensure __all__ is always defined"""
69 69 mod = import_module(self._mirror)
70 70 try:
71 71 return mod.__all__
72 72 except AttributeError:
73 73 return [name for name in dir(mod) if not name.startswith('_')]
74 74
75 75 def __getattr__(self, key):
76 76 # Use the equivalent of import_item(name), see below
77 77 name = "%s.%s" % (self._mirror, key)
78 78 try:
79 79 return import_item(name)
80 80 except ImportError as e:
81 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}>"
General Comments 0
You need to be logged in to leave comments. Login now