Issue
In a Jupyter notebook there are some built-in magics that change the contents of a notebook cell. For example, the %load
magic replaces the contents of the current cell with the contents of a file on the file system.
How can I write a custom magic command that does something similar?
What I have so far prints something to stdout
def tutorial_asset(line):
print('hello world')
def load_ipython_extension(ipython):
ipython.register_magic_function(tutorial_asset, 'line')
And I can load it with %load_ext tutorial_asset
. But from there I'm lost.
[Edit]:
I've found a way to get to the interactive shell instance:
@magics_class
class MyMagics(Magics):
@line_magic
def tutorial_asset(self, parameters):
self.shell
The self.shell
object seems to give complete access to the set of cells in the notebook, but the only way I can find to modify the cells is to do self.shell.set_next_input('print("hello world")')
. This isn't sufficient because, in a Jupyter notebook, that input cell is skipped, and it doesn't overwrite the input cell, it instead creates a new input cell after it.
This would be fine, but if I run the notebook a second time, it creates another input cell with the same file loaded, which is annoying. Can I have it load only once, say, by checking if the contents are already in the next cell?
Solution
EDIT: After a little further digging, I found that the current build of notebook cannot do both.
Well, this is a little tricky... Looking at the IPython code, it looks like you need to use set_next_input
if you want to replace the cell, and run_cell
if you actually want to run some code. However, I can't get both to work at once - it looks like set_next_input
always wins.
Digging into the code, the web front-end supports optional clearing of the output on set_next_input
. However, the kernel doesn't yet support setting this flag (and so output will always be cleared as the default action). To do better will require a patch to ipykernel.
The best I therefore have is the following code, using jupyter notebook version 4.2.1:
from __future__ import print_function
from IPython import get_ipython
from IPython.core.magic import Magics, magics_class, line_magic
@magics_class
class MyMagics(Magics):
@line_magic
def lmagic(self, line):
"Replace current line with new output"
raw_code = 'print("Hello world!")'
# Comment out this line if you actually want to run the code.
self.shell.set_next_input('# %lmagic\n{}'.format(raw_code), replace=True)
# Uncomment this line if you want to run the code instead.
# self.shell.run_cell(raw_code, store_history=False)
ip = get_ipython()
ip.register_magics(MyMagics)
This gives you a magic command lmagic
that will either replace the current cell or run the raw_code
depending on which bit of the code you have commented out.
Answered By - Peter Brittain
0 comments:
Post a Comment
Note: Only a member of this blog may post a comment.