| |
| """A class for managing IPython extensions.""" |
|
|
| |
| |
|
|
| import os |
| import os.path |
| import sys |
| from importlib import import_module, reload |
|
|
| from traitlets.config.configurable import Configurable |
| from IPython.utils.path import ensure_dir_exists |
| from traitlets import Instance |
|
|
|
|
| |
| |
| |
|
|
| BUILTINS_EXTS = {"storemagic": False, "autoreload": False} |
|
|
|
|
| class ExtensionManager(Configurable): |
| """A class to manage IPython extensions. |
| |
| An IPython extension is an importable Python module that has |
| a function with the signature:: |
| |
| def load_ipython_extension(ipython): |
| # Do things with ipython |
| |
| This function is called after your extension is imported and the |
| currently active :class:`InteractiveShell` instance is passed as |
| the only argument. You can do anything you want with IPython at |
| that point, including defining new magic and aliases, adding new |
| components, etc. |
| |
| You can also optionally define an :func:`unload_ipython_extension(ipython)` |
| function, which will be called if the user unloads or reloads the extension. |
| The extension manager will only call :func:`load_ipython_extension` again |
| if the extension is reloaded. |
| |
| You can put your extension modules anywhere you want, as long as |
| they can be imported by Python's standard import mechanism. |
| """ |
|
|
| shell = Instance('IPython.core.interactiveshell.InteractiveShellABC', allow_none=True) |
|
|
| def __init__(self, shell=None, **kwargs): |
| super(ExtensionManager, self).__init__(shell=shell, **kwargs) |
| self.loaded = set() |
|
|
| def load_extension(self, module_str: str): |
| """Load an IPython extension by its module name. |
| |
| Returns the string "already loaded" if the extension is already loaded, |
| "no load function" if the module doesn't have a load_ipython_extension |
| function, or None if it succeeded. |
| """ |
| try: |
| return self._load_extension(module_str) |
| except ModuleNotFoundError: |
| if module_str in BUILTINS_EXTS: |
| BUILTINS_EXTS[module_str] = True |
| return self._load_extension("IPython.extensions." + module_str) |
| raise |
|
|
| def _load_extension(self, module_str: str): |
| if module_str in self.loaded: |
| return "already loaded" |
|
|
| assert self.shell is not None |
|
|
| with self.shell.builtin_trap: |
| if module_str not in sys.modules: |
| mod = import_module(module_str) |
| mod = sys.modules[module_str] |
| if self._call_load_ipython_extension(mod): |
| self.loaded.add(module_str) |
| else: |
| return "no load function" |
|
|
| def unload_extension(self, module_str: str): |
| """Unload an IPython extension by its module name. |
| |
| This function looks up the extension's name in ``sys.modules`` and |
| simply calls ``mod.unload_ipython_extension(self)``. |
| |
| Returns the string "no unload function" if the extension doesn't define |
| a function to unload itself, "not loaded" if the extension isn't loaded, |
| otherwise None. |
| """ |
| if BUILTINS_EXTS.get(module_str, False) is True: |
| module_str = "IPython.extensions." + module_str |
| if module_str not in self.loaded: |
| return "not loaded" |
|
|
| if module_str in sys.modules: |
| mod = sys.modules[module_str] |
| if self._call_unload_ipython_extension(mod): |
| self.loaded.discard(module_str) |
| else: |
| return "no unload function" |
|
|
| def reload_extension(self, module_str: str): |
| """Reload an IPython extension by calling reload. |
| |
| If the module has not been loaded before, |
| :meth:`InteractiveShell.load_extension` is called. Otherwise |
| :func:`reload` is called and then the :func:`load_ipython_extension` |
| function of the module, if it exists is called. |
| """ |
|
|
| if BUILTINS_EXTS.get(module_str, False) is True: |
| module_str = "IPython.extensions." + module_str |
|
|
| if (module_str in self.loaded) and (module_str in sys.modules): |
| self.unload_extension(module_str) |
| mod = sys.modules[module_str] |
| reload(mod) |
| if self._call_load_ipython_extension(mod): |
| self.loaded.add(module_str) |
| else: |
| self.load_extension(module_str) |
|
|
| def _call_load_ipython_extension(self, mod): |
| if hasattr(mod, 'load_ipython_extension'): |
| mod.load_ipython_extension(self.shell) |
| return True |
|
|
| def _call_unload_ipython_extension(self, mod): |
| if hasattr(mod, 'unload_ipython_extension'): |
| mod.unload_ipython_extension(self.shell) |
| return True |
|
|