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?
1 Answers
Answers 1
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.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.
0 comments:
Post a Comment