gregsexton / ob-ipython

org-babel integration with Jupyter for evaluation of (Python by default) code blocks
737 stars 111 forks source link

+TITLE: Readme

** What is this?

An Emacs library that allows Org mode to evaluate code blocks using a Jupyter kernel (Python by default).

** Screenshot

[[./screenshot.jpg]]

** How do I install this?

*** First, you need IPython

Before installing, you'll need Jupyter (>= 1.0) and IPython (>=
5.0) installed and working. You will also need the [[http://jupyter.readthedocs.org/en/latest/install.html][Jupyter]] console
and client (~jupyter_console~, ~jupyter_client~) libraries. All of
this should be trivially installable using pip.

*** Install the Emacs plugin

This package is in MELPA. I recommend installing from there.

Otherwise, for manual installation, you'll need the following
elisp 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)~.

Lastly, activate ~ipython~ in Org-Babel:

#+BEGIN_SRC emacs-lisp
  (org-babel-do-load-languages
   'org-babel-load-languages
   '((ipython . t)
     ;; other languages..
     ))
#+END_SRC

** How do I use it?

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. You can also pass a connection json of an existing ipython session as a session name in order to connect to it.

The result returned by ob-ipython should be renderable by org so it's recommended to always use ~:results raw drawer~.

+BEGIN_SRC org

 ,#+BEGIN_SRC ipython :session :results raw drawer
   %matplotlib inline
   import matplotlib.pyplot as plt
   import numpy as np
 ,#+END_SRC

+END_SRC

Here we evaluate some code with a function definition using a named session.

+BEGIN_SRC org

 ,#+BEGIN_SRC ipython :session mysession :exports both :results raw drawer
   def foo(x):
       return x + 9

   [foo(x) + 7 for x in range(7)]
 ,#+END_SRC

 ,#+RESULTS:
 : [16, 17, 18, 19, 20, 21, 22]

+END_SRC

To get a graphic out, you must ensure that you have evaluated ~%matplotlib inline~ first. A file will be generated for you (see the ~ob-ipython-resources-dir~ custom var if you want to change the path).

+BEGIN_SRC org

 ,#+BEGIN_SRC ipython :session :exports both :results raw drawer
   plt.hist(np.random.randn(20000), bins=200)
 ,#+END_SRC

+END_SRC

If you provide an ipyfile argument, this filename will be used instead of generating one.

+BEGIN_SRC org

 ,#+BEGIN_SRC ipython :session :ipyfile /tmp/image.png :exports both :results raw drawer
   plt.hist(np.random.randn(20000), bins=200)
 ,#+END_SRC

+END_SRC

In order to make an svg graphic rather than a png, you may specify the output format globally to IPython.

+BEGIN_EXAMPLE

 %config InlineBackend.figure_format = 'svg'

+END_EXAMPLE

If you wish to use a specific Jupyter kernel, you may pass the kernel option. This enables you to use ob-ipython with languages other than Python. You need to have the Jupyter kernel installed and working before you can use this.

When mixing code from different languages you will need to make use of the session argument.

+BEGIN_SRC org

 ,#+BEGIN_SRC ipython :session :kernel clojure
   (+ 1 2)
 ,#+END_SRC

 ,#+RESULTS:
 : 3

+END_SRC

ob-ipython supports providing variables and even tables to code.

+BEGIN_SRC org

 ,#+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]])

+END_SRC

Asynchronous execution is supported. Use the ~:async t~ option.

+BEGIN_SRC org

 ,#+BEGIN_SRC ipython :session :ipyfile /tmp/image.png :exports both :async t :results raw drawer
   import time
   time.sleep(3)
   plt.hist(np.random.randn(20000), bins=200)
 ,#+END_SRC

+END_SRC

** Experimental: Jupyter kernel support

This package is starting to transition from the original ipython-only support to full jupyter support.

If you have other kernels installed, you should be able to evaluate blocks by providing jupyter-X as the language, where X is the language name recognised by jupyter. For example, you can do something like this:

+BEGIN_SRC org

 ,#+BEGIN_SRC jupyter-R :results raw drawer
   x <- 3
   x
 ,#+END_SRC

+END_SRC

Notice, when providing languages like this, you do not need to (although you may) provide a session argument. A default session is created per language. This should also try to provide support for per-language modes when editing.

** Working with a remote session

First, follow the instructions [[https://github.com/ipython/ipython/wiki/Cookbook:-Connecting-to-a-remote-kernel-via-ssh][here]] to get access to a remote kernel. You can then pass the name of the local json file as a session arg to use this tunnel.

Essentially the instructions boil down to

** What features are there outside of Org SRC block evaluation?

** Tips and tricks

Here are a few things I have setup to make life better. These aren't provided with ob-ipython, but are recommended.

** Help, it doesn't work

First thing to do is check that you have all of the required dependencies. Several common problems have been resolved in the project's issues, so take a look there to see if your problem has a quick fix. Otherwise feel free to cut an issue - I'll do my best to help.

** Alternatives *** Why not use IPython notebook?

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 [[https://millejoh.github.io/emacs-ipython-notebook/][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.

*** How does this compare to regular Org Python integration (ob-python)?

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~.