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