Creating a New Magic

This guide explains how to write a custom magic for any MetaKernel-based kernel — whether you are a kernel author bundling magics with your kernel, or an end-user who wants to add a magic to an existing kernel.

Magic file conventions

Each magic lives in its own file named {name}_magic.py. The file must:

  1. Define a class that inherits from metakernel.Magic.

  2. Implement one or both of:

    • line_{name}(self, ...) — invoked by %name arg1 arg2

    • cell_{name}(self, ...) — invoked by %%name arg1\ncell body

  3. Expose a module-level register_magics(kernel) function so that MetaKernel can load it automatically.

A minimal example

# greet_magic.py

from metakernel import Magic, MetaKernel


class GreetMagic(Magic):

    def line_greet(self, name="world"):
        """
        %greet [name]

        Print a friendly greeting.

        Example::

            %greet Alice
        """
        self.kernel.Print(f"Hello, {name}!")


def register_magics(kernel: MetaKernel) -> None:
    kernel.register_magics(GreetMagic)

Save this file and use it as described in the next section.

Installing a magic

There are two ways to make a magic available in your kernel.

User-local installation (any kernel)

Drop the magic file into ~/.ipython/metakernel/magics/. MetaKernel creates this directory automatically and searches it on every startup.

cp greet_magic.py ~/.ipython/metakernel/magics/

Then, in a running notebook cell, reload the magic registry:

%reload_magics

The new magic is now available for the rest of the session without restarting the kernel. On future kernel starts it will be loaded automatically.

Downloading from a URL

Use the built-in %install_magic line magic to fetch a magic file directly from a URL and install it into your local magic directory:

%install_magic https://example.com/path/to/greet_magic.py

%install_magic downloads the file and then runs %reload_magics for you automatically.

Bundling with a kernel package

Place the magic file inside a magics/ subpackage alongside your kernel module. MetaKernel discovers these at startup via reload_magics():

my_kernel/
├── __init__.py
└── magics/
    └── greet_magic.py

No extra registration step is required — the register_magics(kernel) function in each file is called automatically.

Writing a cell magic

A cell magic receives the rest of the cell as a body via self.code. Set self.evaluate = False if you do not want the kernel to evaluate the cell body as normal code after the magic runs.

# repeat_magic.py

from metakernel import Magic, MetaKernel


class RepeatMagic(Magic):

    def cell_repeat(self, times=2):
        """
        %%repeat [times]

        Print the cell body *times* times.

        Example::

            %%repeat 3
            Hello!
        """
        for _ in range(int(times)):
            self.kernel.Print(self.code)
        self.evaluate = False   # don't pass the body to the kernel


def register_magics(kernel: MetaKernel) -> None:
    kernel.register_magics(RepeatMagic)

Adding options with @option

Use the metakernel.option() decorator (backed by optparse) to add named flags to a magic. The decorator appends option documentation to the magic’s docstring automatically.

# shout_magic.py

from metakernel import Magic, MetaKernel, option


class ShoutMagic(Magic):

    @option(
        "-u", "--upper",
        action="store_true",
        default=False,
        help="Convert output to upper case.",
    )
    def line_shout(self, message="hello", upper=False):
        """
        %shout [-u] message

        Print a message, optionally in upper case.

        Example::

            %shout -u hello world
        """
        if upper:
            message = message.upper()
        self.kernel.Print(message)


def register_magics(kernel: MetaKernel) -> None:
    kernel.register_magics(ShoutMagic)

Options can also be used on cell magics — see tutor_magic.py in the MetaKernel source for a complete example.

Kernel output helpers

Inside a magic method, use the following helpers on self.kernel:

self.kernel.Print(text)

Send plain text to the notebook output area.

self.kernel.Error(text)

Send error text (displayed in red by most frontends).

self.kernel.Display(obj)

Display any rich IPython displayable (e.g. IPython.display.HTML, IFrame, images).

self.kernel.set_variable(name, value)

Inject a variable into the kernel’s namespace so the user can access it after the magic runs.

self.kernel.get_variable(name)

Read a variable from the kernel’s namespace.

self.code

The raw cell body (cell magics only); available after call_magic sets it, or directly inside cell_* methods.

self.evaluate

Boolean (default True). Set to False inside a cell magic to prevent the kernel from evaluating self.code as normal code.

Docstrings and help

The docstring of a magic method is displayed when the user runs:

%greet?
%%greet?

It is also executed as a doctest by pytest (--doctest-modules), so keep examples valid. A good docstring follows this structure:

%magic_name [args]

One-line summary.

Longer explanation if needed.

Example::

    %magic_name value

IPython / Jupyter notebook compatibility

If you want your magic to work in plain IPython (not just in MetaKernel-based kernels), add a register_ipython_magics() function to the file:

def register_ipython_magics() -> None:
    from metakernel import IPythonKernel
    from metakernel.magic import register_line_magic

    kernel = IPythonKernel()
    magic = GreetMagic(kernel)

    @register_line_magic
    def greet(line: str) -> None:
        magic.line_greet(line or "world")

MetaKernel calls register_ipython_magics() automatically when loading magics into an IPython session. For cell magics, use metakernel.magic.register_cell_magic() instead.

See tutor_magic.py for a complete working example of both functions.