Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/IPython/core/extensions.py: 31%

70 statements  

« prev     ^ index     » next       coverage.py v7.4.4, created at 2024-04-20 06:09 +0000

1# encoding: utf-8 

2"""A class for managing IPython extensions.""" 

3 

4# Copyright (c) IPython Development Team. 

5# Distributed under the terms of the Modified BSD License. 

6 

7import os 

8import os.path 

9import sys 

10from importlib import import_module, reload 

11 

12from traitlets.config.configurable import Configurable 

13from IPython.utils.path import ensure_dir_exists, compress_user 

14from IPython.utils.decorators import undoc 

15from traitlets import Instance 

16 

17 

18#----------------------------------------------------------------------------- 

19# Main class 

20#----------------------------------------------------------------------------- 

21 

22BUILTINS_EXTS = {"storemagic": False, "autoreload": False} 

23 

24 

25class ExtensionManager(Configurable): 

26 """A class to manage IPython extensions. 

27 

28 An IPython extension is an importable Python module that has 

29 a function with the signature:: 

30 

31 def load_ipython_extension(ipython): 

32 # Do things with ipython 

33 

34 This function is called after your extension is imported and the 

35 currently active :class:`InteractiveShell` instance is passed as 

36 the only argument. You can do anything you want with IPython at 

37 that point, including defining new magic and aliases, adding new 

38 components, etc. 

39  

40 You can also optionally define an :func:`unload_ipython_extension(ipython)` 

41 function, which will be called if the user unloads or reloads the extension. 

42 The extension manager will only call :func:`load_ipython_extension` again 

43 if the extension is reloaded. 

44 

45 You can put your extension modules anywhere you want, as long as 

46 they can be imported by Python's standard import mechanism. However, 

47 to make it easy to write extensions, you can also put your extensions 

48 in ``os.path.join(self.ipython_dir, 'extensions')``. This directory 

49 is added to ``sys.path`` automatically. 

50 """ 

51 

52 shell = Instance('IPython.core.interactiveshell.InteractiveShellABC', allow_none=True) 

53 

54 def __init__(self, shell=None, **kwargs): 

55 super(ExtensionManager, self).__init__(shell=shell, **kwargs) 

56 self.shell.observe( 

57 self._on_ipython_dir_changed, names=('ipython_dir',) 

58 ) 

59 self.loaded = set() 

60 

61 @property 

62 def ipython_extension_dir(self): 

63 return os.path.join(self.shell.ipython_dir, u'extensions') 

64 

65 def _on_ipython_dir_changed(self, change): 

66 ensure_dir_exists(self.ipython_extension_dir) 

67 

68 def load_extension(self, module_str: str): 

69 """Load an IPython extension by its module name. 

70 

71 Returns the string "already loaded" if the extension is already loaded, 

72 "no load function" if the module doesn't have a load_ipython_extension 

73 function, or None if it succeeded. 

74 """ 

75 try: 

76 return self._load_extension(module_str) 

77 except ModuleNotFoundError: 

78 if module_str in BUILTINS_EXTS: 

79 BUILTINS_EXTS[module_str] = True 

80 return self._load_extension("IPython.extensions." + module_str) 

81 raise 

82 

83 def _load_extension(self, module_str: str): 

84 if module_str in self.loaded: 

85 return "already loaded" 

86 

87 from IPython.utils.syspathcontext import prepended_to_syspath 

88 

89 with self.shell.builtin_trap: 

90 if module_str not in sys.modules: 

91 mod = import_module(module_str) 

92 mod = sys.modules[module_str] 

93 if self._call_load_ipython_extension(mod): 

94 self.loaded.add(module_str) 

95 else: 

96 return "no load function" 

97 

98 def unload_extension(self, module_str: str): 

99 """Unload an IPython extension by its module name. 

100 

101 This function looks up the extension's name in ``sys.modules`` and 

102 simply calls ``mod.unload_ipython_extension(self)``. 

103 

104 Returns the string "no unload function" if the extension doesn't define 

105 a function to unload itself, "not loaded" if the extension isn't loaded, 

106 otherwise None. 

107 """ 

108 if BUILTINS_EXTS.get(module_str, False) is True: 

109 module_str = "IPython.extensions." + module_str 

110 if module_str not in self.loaded: 

111 return "not loaded" 

112 

113 if module_str in sys.modules: 

114 mod = sys.modules[module_str] 

115 if self._call_unload_ipython_extension(mod): 

116 self.loaded.discard(module_str) 

117 else: 

118 return "no unload function" 

119 

120 def reload_extension(self, module_str: str): 

121 """Reload an IPython extension by calling reload. 

122 

123 If the module has not been loaded before, 

124 :meth:`InteractiveShell.load_extension` is called. Otherwise 

125 :func:`reload` is called and then the :func:`load_ipython_extension` 

126 function of the module, if it exists is called. 

127 """ 

128 from IPython.utils.syspathcontext import prepended_to_syspath 

129 

130 if BUILTINS_EXTS.get(module_str, False) is True: 

131 module_str = "IPython.extensions." + module_str 

132 

133 if (module_str in self.loaded) and (module_str in sys.modules): 

134 self.unload_extension(module_str) 

135 mod = sys.modules[module_str] 

136 with prepended_to_syspath(self.ipython_extension_dir): 

137 reload(mod) 

138 if self._call_load_ipython_extension(mod): 

139 self.loaded.add(module_str) 

140 else: 

141 self.load_extension(module_str) 

142 

143 def _call_load_ipython_extension(self, mod): 

144 if hasattr(mod, 'load_ipython_extension'): 

145 mod.load_ipython_extension(self.shell) 

146 return True 

147 

148 def _call_unload_ipython_extension(self, mod): 

149 if hasattr(mod, 'unload_ipython_extension'): 

150 mod.unload_ipython_extension(self.shell) 

151 return True