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
« 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."""
4# Copyright (c) IPython Development Team.
5# Distributed under the terms of the Modified BSD License.
7import os
8import os.path
9import sys
10from importlib import import_module, reload
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
18#-----------------------------------------------------------------------------
19# Main class
20#-----------------------------------------------------------------------------
22BUILTINS_EXTS = {"storemagic": False, "autoreload": False}
25class ExtensionManager(Configurable):
26 """A class to manage IPython extensions.
28 An IPython extension is an importable Python module that has
29 a function with the signature::
31 def load_ipython_extension(ipython):
32 # Do things with ipython
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.
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.
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 """
52 shell = Instance('IPython.core.interactiveshell.InteractiveShellABC', allow_none=True)
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()
61 @property
62 def ipython_extension_dir(self):
63 return os.path.join(self.shell.ipython_dir, u'extensions')
65 def _on_ipython_dir_changed(self, change):
66 ensure_dir_exists(self.ipython_extension_dir)
68 def load_extension(self, module_str: str):
69 """Load an IPython extension by its module name.
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
83 def _load_extension(self, module_str: str):
84 if module_str in self.loaded:
85 return "already loaded"
87 from IPython.utils.syspathcontext import prepended_to_syspath
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"
98 def unload_extension(self, module_str: str):
99 """Unload an IPython extension by its module name.
101 This function looks up the extension's name in ``sys.modules`` and
102 simply calls ``mod.unload_ipython_extension(self)``.
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"
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"
120 def reload_extension(self, module_str: str):
121 """Reload an IPython extension by calling reload.
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
130 if BUILTINS_EXTS.get(module_str, False) is True:
131 module_str = "IPython.extensions." + module_str
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)
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
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