There's a general problem for writing package-specific extensions to IPython.
Namely, you want to be able to write and enable an extension to IPython that
provides special behavior for particular objects, say numpy arrays, without
requiring that a particular package be imported at startup. You can often get by
in ad hoc ways. For example, in the pretty-printing code, I added the ability to
register deferred type-checking. For magic functions, you can usually just do
local imports inside the magics.
However, I've had an idea for a general solution.
Let's say you want to make an extension that does something special with numpy
arrays. Perhaps it alters the ? introspection to print out array metadata like
shape and dtype instead of the ndarray docstring when you use ? on arrays. You
don't want it to modify anything until numpy is imported. You also don't want to
import numpy at startup every time. You could implement a deferred isinstance()
check the way I do it in pretty.py, but that's a complicated procedure that
would need to be replicated everywhere.
Instead, we could have an API for our configuration for deferred extensions,
namely, a list of (module_name, extension_name) pairs. IPython would keep a
registry mapping these module names to the extension names. IPython's execution
loop can be modified to check sys.modules after every execution and activate
every extension whose requisite modules appear in sys.modules. Then it removes
those extensions from the list.
So if my extension is named ipy_numpy_oinspect.py, then I'd have something like
the following in my ipython_config.py file:
c.Global.deferred_extensions = [
# (module_to_wait_for, extension)
('numpy', 'ipy_numpy_oinspect'),
]
And somewhere in run_cell() after the run_ast() call:
# Check for any deferred extensions.
# FIXME: probably wrap this up in a method.
loaded = []
for modname, extname in self.extension_manager.deferred_extensions:
# Explicitly check for is not None since sometimes a missing module
# will get a None entry to let the import mechanism fail faster.
if sys.modules.get(modname, None) is not None:
self.extension_manager.load_extension(extname)
loaded.append((modname, extname))
for x in loaded:
self.extension_manager.deferred_extensions.remove(x)
--
Robert Kern
"I have come to believe that the whole world is an enigma, a harmless enigma
that is made terrible by our own mad attempt to interpret it as though it had
an underlying truth."
-- Umberto Eco