Todo¶
Subcommands in IPython and Jupyter.¶
There’s a ton that could be done here. Well I guess sub-apps would be a better name for it but whatever.
Mostly here to note that I think the ‘editor’ hooks
doesn’t work the way it’s supposed to if you want to configure it.
If you run %edit
in the shell it doesn’t provide any arguments, but
it’ll complain that it needs a function that accepts 4 positional parameters.
I’m pretty confident it isn’t just me calling the function wrong too. Try this.:
from IPython import get_ipython
ip = get_ipython()
ip.hooks['editor'].chain
# inspect your hooks look normal
from IPython.core.hooks import fix_error_editor
ip.hooks['editor'].add(fix_error_editor)
%edit # --> complains that it needs 3 more positional parameters
Before I noticed this, I wrote a function that only accepts 1 positional arg, which btw…it was the filename duh. I don’t know or care what column number I start at??? and takes optional keyword arguments.
Said the function was getting called wrong as it only accepted 1 positional and was being called with 4. sigh.
IPython history¶
-
history
¶
Here’s some seemingly inconsistent behavior.
$: ipython history
No subcommand specified. Must specify one of: dict_keys(['trim', 'clear'])
Manage the IPython history database.
Subcommands
-----------
Subcommands are launched as `ipython-history cmd [args]`. For information
on using subcommand 'cmd', do: `ipython-history cmd -h`.
So there we have instructions to use the invocation ipython-history
.
$: ipython-history -h
ipython-history: command not found
Erhm. That’s confusing.
So how does the history
command work?
IPython History API¶
Interact with the IPython sqlite3
database.
-
trim
¶
Trim the IPython history database to the last 1000 entries.
-
clear
¶
Clear the IPython history database, deleting all entries.
Also the original implementation only defines 2 options for the subcommand.
But it would be nice to have options like backup
and grep
or something. Though to be fair the :class:`~IPython.utils.text.SList` class
has a ‘grep’ method.
There are a handful of nice to have but ultimately pointless functions in
IPython.utils
so why not take advantage?
Writing Magics For Our Users¶
In the documentation, it specifies the requirements for a magic.
And I quote the custommagics
document.:
There are two main ways to define your own magic functions: from standalone functions and by inheriting from a base class provided by IPython,
Magics
.
It then gives an example.
from IPython.core.magic import (Magics, magics_class,
line_magic,cell_magic, line_cell_magic)
# The class MUST call this class decorator at creation time
@magics_class
class MyMagics(Magics):
@line_magic
def lmagic(self, line):
"my line magic"
print("Full access to the main IPython object:", self.shell)
print("Variables in the user namespace:", list(self.shell.user_ns.keys()))
return line
@cell_magic
def cmagic(self, line, cell):
"my cell magic"
return line, cell
@line_cell_magic
def lcmagic(self, line, cell=None):
"Magic that works both as %lmagic and as %%cmagic"
if cell is None:
print("Called as line magic")
return line
else:
print("Called as cell magic")
return line, cell
# In order to actually use these magics, you must register them with a
# running IPython instance.
def load_ipython_extension(ipython):
"""
Any module file that define a function named `load_ipython_extension`
can be loaded via `%load_ext module.path` or be configured to be
autoloaded by IPython at startup time.
"""
# You can register the class itself without instantiating it. IPython will
# call the default constructor on it.
ipython.register_magics(MyMagics)
How can we rewrite the magic implementation so that the decorator magics_class
isn’t required anymore?
Like if they pass us a string can we not just feed it to our own home-brewed wrapper function? Off the top of my head I’m guessing something like this.:
arg, _ = sys.argv[1:]
if not hasattr(arg, 'load_ext'): # or whatever interface is expected
@magics_class
@functools.wraps
def wrapped(*args, **kwargs):
return *args, **kwargs
shell.register_magic('MyMagic') # <---- incorrectly passed as a str
# But in the register_magic method we would do:
class InteractiveShell:
...
def register_magic(self, *args, **kwargs):
# Run that interface check with
if not hasattr(arg, 'load_ext'): # or whatever interface is expected
# and then call the wrapped function with the args that were passed to us
...
# the usual stuff
I’m sure that I poorly executed that here; however, after some deliberation, would it be that hard to do?
-
%alias_magic
¶ Create an alias for an already definedmagic.
%alias_magic
is really convenient and makes it possible to create really
short monikers for rather complicated mixes of shell scripts and object-oriented
python. But it doesn’t copy over the __doc__ from the old magic.
There’s a ton of good information that gets lost going from %edit
to %ed
and
%history
to %hist
. Anything we can do about that?