|
|
|
"""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 |
|
|