IPython magic to edit Jupyter cell tags

I came up with idea, that it would be cool to edit cell tags with help of IPython magic instead of mouse clicking and interacting with tags or metadata toolbars. So, now I can do it by typing this code directly into the cell input area:

%tags foo bar baz

Navigate to this gist to see the code snippet (MIT License).

The Jupyter project is an amazing tool for interactive programming and data visualization. When it comes to presenting the result of the programming efforts done in your Jupyter notebook there are tools such as nbconvert and friends, which allows to export result of the work into PDF or HTML format. Often one wants to polish the notebook for generating a report, for example by hiding certain code cells or doing other preprocessing during convert. For these purposes Jupyter supports assigning additional metadata or tags to the notebook cells.

However, Jupyter UI for managing cell metadata is a bit annoying, because it involves a lot of mouse clicking. I like to keep my hands on the keyboard and use shortcuts for doing things, when possible. A better way is to store this information directly in the code cell. Python kernel (IPython) for Jupyter notebook supports so called %magic commands, which allows to treat corresponding source line in any custom way.

To change cell metadata we need to talk to the Jupyter kernel. A found out that this is possible from the javascript code. To inject it into the notebook we can use special display object, for example:

from IPython.display import Javascript, display
display(Javascript('alert("hi!")'))

Within javascript code, according to the manual: «the containing element will be available as element».

Now we can write a helper function that would find corresponding input cell and modify its metadata:

define('setTags', function() {
    return function(element, tags) {
        var cell_element = element.parents('.cell');
        var index = Jupyter.notebook.get_cell_elements().index(cell_element);
        var cell = Jupyter.notebook.get_cell(index);
        cell.metadata.tags = tags;
    }
});

To manipulate functions scope I use requirejs library available in jupyter.
Finally, we define custom %magic which uses this function.

from IPython.core.magic import register_cell_magic, register_line_cell_magic
from IPython.display import Javascript, display
import json

def _set_tags(tags):
    assert all(map(lambda t: isinstance(t, str), tags))
    display(Javascript(
        """
        require(['setTags'], function(setTags) {
            setTags(element, %s);
        });
        """ % json.dumps(tags)
    ))

@register_line_cell_magic
def tag(line, cell=None):
    _set_tags(line.split())

In the end there is a screenshot of all steps described above:

Example notebook

It is possible to define above commands in custom.js and a python module to share the code across different notebooks. Known issue: tag toolbar doesn’t get updated automatically.

© Habrahabr.ru