Jupyter: Write a custom magic that modifies the contents of the cell it's in

倾然丶 夕夏残阳落幕 提交于 2020-01-12 02:20:04

问题


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:


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.



来源:https://stackoverflow.com/questions/38020679/jupyter-write-a-custom-magic-that-modifies-the-contents-of-the-cell-its-in

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!