An Emacs library that allows Org mode to evaluate Python code blocks using an IPython kernel.
I tried using the IPython notebook but quickly became frustrated with trying to write code in a web browser. This provides another option for creating documents containing executable Python code, but in Emacs - with everything that entails.
Why not use EIN?
EIN is really great. It kept me happy for quite a while but I started to feel constrained by the cell format of IPython notebooks. What I really wanted was to embed code in Org documents. It’s hard to compete with Org mode! A few key points in favour of Org:
- In my opinion, Org’s markup is better than Markdown.
- Org’s organisational, editing and navigation facilities are much better than EIN.
- Org’s tables…
- Org can export to multiple formats.
- I like how Org opens a new buffer when editing code so that you can use a Python major mode rather than trying to handle multiple major modes in one.
I also found myself hitting bugs in EIN where evaluation and doc lookup would just stop working. I regularly had to kill and reopen buffers or restart the IPython kernel and this was getting frustrating.
I think this is more robust. The executed code is sent to a running IPython kernel which has an architecture designed for this purpose. The way ob-python works feels like a bit of a hack. I ran in to race conditions using ob-python where the Org buffer would update its results before the Python REPL had finished evaluating the code block. This is what eventually drove me to write this.
It’s easier to get plots and images out of this. I also provide several features I missed when using plain ob-python, such as looking up documentation and getting IPython-style tracebacks when things go wrong.
You can also use IPython-specific features such as %timeit
.
Before installing, you’ll need IPython installed and working. You will also need the Tornado library. This is a dependency of ipython-notebook, if you already have that.
This package will probably be in MELPA by the time you read this. I recommend installing from there. For manual installation, you’ll need the following dependencies first:
- https://github.com/magnars/dash.el
- Including dash-functional
- https://github.com/magnars/s.el
- https://github.com/rejeep/f.el
Then just drop this somewhere in your load path and (require 'ob-ipython)
.
Open an org file, add a SRC block and evaluate as you would any Org
SRC block (usually C-c C-c
). Here I will run through some example
blocks.
This is the most basic ipython block. You must provide a session argument. You can name the session if you wish to separate state.
#+BEGIN_SRC ipython :session
%matplotlib inline
import matplotlib.pyplot as plt
import numpy as np
#+END_SRC
Here we evaluate some code with a function definition using a named session.
#+BEGIN_SRC ipython :session mysession :exports both
def foo(x):
return x + 9
[foo(x) + 7 for x in range(7)]
#+END_SRC
#+RESULTS:
: [16, 17, 18, 19, 20, 21, 22]
This is how you can get a graphic out. Notice the file argument.
This must be provided. You must also ensure that you have evaluated
%matplotlib inline
before evaluating this.
#+BEGIN_SRC ipython :session :file /tmp/image.png :exports both
plt.hist(np.random.randn(20000), bins=200)
#+END_SRC
ob-ipython supports providing variables and even tables to code.
#+TBLNAME: data_table
| a | 1 | 2 |
| b | 2 | 3 |
| c | 3 | 4 |
#+BEGIN_SRC ipython :session :exports both :var x=2 :var data=data_table
(x, data)
#+END_SRC
#+RESULTS:
: (2, [['a', 1, 2], ['b', 2, 3], ['c', 3, 4]])
- You can ask the running IPython kernel for documentation. Open a
SRC block, place the point on the thing you’re interested in and
run
M-x ob-ipython-inspect
. I recommend you bind this to a key. - It’s often easier to play with code using a REPL. With the point
in an ipython SRC block, you can open a REPL connected to the
current kernel by running
C-c C-v C-z
. I recommend you do this anyway, as python-mode can now use this REPL to provide completion in code buffers. - If evaluated code produces an error, this will be displayed nicely in a buffer using IPython’s traceback support.
- Stdout from code evaluation is displayed in a popup buffer. This is great for debugging or getting verbose output that is best left out of documents.
- You can interrupt or kill a running kernel. This is helpful if
things get stuck or really broken. See
M-x ob-ipython-interrupt-kernel
andM-x ob-ipython-kill-kernel
, respectively.
Here are a few things I’ve setup to make life better. These aren’t provided with ob-ipython but are recommended.
- Be sure to use
%matplotlib inline
, otherwise graphics won’t work. - I use yasnippet to create src blocks. Here is the snippet I use.
It takes care of generating unique file names (when I want one)
so I don’t have to think about this.
# -*- mode: snippet -*- # name: ipython block # key: py # -- #+BEGIN_SRC ipython :session ${1::file ${2:$$(let ((temporary-file-directory "./")) (make-temp-file "py" nil ".png"))} }:exports ${3:both} $0 #+END_SRC
- I use the following Org settings:
(setq org-confirm-babel-evaluate nil) ;don't prompt me to confirm everytime I want to evaluate a block ;;; display/update images in the buffer after I evaluate (add-hook 'org-babel-after-execute-hook 'org-display-inline-images 'append)
- Open a REPL using
C-c C-v C-z
so that you get completion in Python buffers.