Creating Magic Functions in IPython — Part 1

You can run %debug right after the exception happened and IPython will start an interactive debugger for that exception.

It’s called post-mortem debugging and I absolutely love it!The second type of magic functions are cell magics and they work on a block of code, not on a single line.

They are prefixed with %%.

To close a block of code, when you are inside a cell magic function, hit Enter twice.

Here is an example of timeit function working on a block of code:In [2]: %%timeit elements = range(1000) .

: x = min(elements) .

: y = max(elements) .

: .

:52.

8 µs ± 4.

37 µs per loop (mean ± std.

dev.

of 7 runs, 10000 loops each)Both the line magic and the cell magic can be created by simply decorating a Python function.

Another way is to write a class that inherits from the IPython.

core.

magic.

Magics.

I will cover this second method in a different article.

Creating line magic functionThat’s all the theory.

Now, let’s write our first magic function.

We will start with a line magic and in the second part of this tutorial, we will make a cell magic.

What kind of magic function are we going to create?.Well, let’s make something useful.

I’m from Poland and in Poland we are use Polish notation for writing down mathematical operations.

So instead of writing 2 + 3, we write + 2 3.

And instead of writing (5 − 6) * 7 we write * − 5 6 7¹.

Let’s write a simple Polish notation interpreter.

It will take an expression in Polish notation as input, and output the correct answer.

To keep this example short, I will limit it to only the basic arithmetic operations: +, -, *, and /.

Here is the code that interprets the Polish notation:def interpret(tokens): token = tokens.

popleft() if token == "+": return interpret(tokens) + interpret(tokens) elif token == "-": return interpret(tokens) – interpret(tokens) elif token == "*": return interpret(tokens) * interpret(tokens) elif token == "/": return interpret(tokens) / interpret(tokens) else: return int(token)Next, we will create a %pn magic function that will use the above code to interpret Polish notation.

from collections import dequefrom IPython.

core.

magic import register_line_magic@register_line_magicdef pn(line): """Polish Notation interpreter Usage: >>> %pn + 2 2 4 """ return interpret(deque(line.

split()))And that’s it.

The @register_line_magic decorator turns our pn function into a %pn magic function.

The line parameter contains whatever is passed to the magic function.

If we call it in the following way: %pn + 2 2, line will contain + 2 2.

To make sure that IPython loads our magic function on startup, copy all the code that we just wrote (you can find the whole file on GitHub) to a file inside IPython startup directory.

In my case, I’m saving it in a file called:~/.

ipython/profile_default/startup/magic_functions.

py(name of the file doesn’t matter, but the directory where you put it is important).

Ok, it’s time to test it.

Start IPython and let’s do some Polish math:In [1]: %pn + 2 2Out[1]: 4In [2]: %pn * – 5 6 7Out[2]: -7In [3]: %pn * + 5 6 + 7 8Out[3]: 165Perfect, it works!.Of course, it’s quite rudimentary — it only supports 4 operators, it doesn’t handle exceptions very well, and given that it’s using recursion, it might fail for very long expressions.

Also, the queue module and the interpret function will now be available in your IPython sessions, since whatever code you put in the magic_function.

py file will be run on IPython startup.

But, you just wrote your first magic function!.And it wasn’t so difficult!At this point, you are probably wondering — Why didn’t we just write a standard Python function?.That’s a good question — in this case, we could simply run the following code:In [1]: pn('+ 2 2')Out[1]: 4or even:In [1]: interpret(deque('+ 2 2'.

split()))Out[1]: 4As I said in the beginning, magic functions are usually helper functions.

Their main advantage is that when someone sees functions with the % prefix, it’s clear that it’s a magic function from IPython, not a function defined somewhere in the code or a built-in.

Also, there is no risk that their names collide with functions from Python modules.

ConclusionI hope you enjoyed this short tutorial and if you have questions or if you have a cool magic function that you would like to share — let me know in the comments!Next part of the “IPython magic functions” series is available here and it covers the cell magic functions, line AND cell magic functions and how to make a type checking magic function for IPython.

Footnotes:[1]: It’s a joke.

We don’t use Polish notation in Poland ;)Originally published at switowski.

com on February 1, 2019.