Using Pymacs to integrate Python into Emacs

| categories: emacs, python | tags:

Pymacs is a project that aims to integrate Python into Emacs, and vice versa. In this post, I am going to examine the Python into Emacs integration. I cloned the git repository, ran make install, and setup my init.el file like this, as suggested in the manual.

(add-to-list 'load-path (expand-file-name "Pymacs" starter-kit-dir))
(require 'pymacs)
(autoload 'pymacs-apply "pymacs")
(autoload 'pymacs-call "pymacs")
(autoload 'pymacs-eval "pymacs" nil t)
(autoload 'pymacs-exec "pymacs" nil t)
(autoload 'pymacs-load "pymacs" nil t)
(autoload 'pymacs-autoload "pymacs")

Pymacs provides some mapping of Python modules to emacs-lisp functions. You load modules in emacs-lisp, and then a dash-mangled version of the Python functions are available, in emacs lisp. Here is an example. We will load numpy, and find the maximum element of an array. For comparison, here is the Python script.

import numpy as np
print np.max(np.array([[1, 1], [2, 4]]))

Now, the corresponding emacs version using Pymacs.

(pymacs-load "numpy" "np-")
(np-max (np-array '((1 1) (2 4))))

Neat! The dot notation is basically replaced with dash notation, and we use a lisp list as the argument instead of an array. Otherwise, this looks almost identical. Now, let us consider something more complicated, and get the determinant of the array. We add a PREFIX to the load statement for numpy.linalg similar to what we would do in Python:

import numpy as np
import numpy.linalg as la
print la.det(np.array([[1, 1], [2, 4]]))

And in emacs-lisp:

(pymacs-load "numpy" "np-")
(pymacs-load "numpy.linalg" "la-")
(la-det (np-array '((1 1) (2 4))))

We can call functions from matplotlib to make a figure. For example:

(pymacs-load "matplotlib.pyplot" "plt-")
(let* ((x  '(1 2 3 4))
       (y  (mapcar (lambda (z) (* z z)) x)))
  (plt-plot x y)
  (plt-xlabel "x values")
  (plt-ylabel "x$^2$")
  (plt-savefig "plt-pymacs.png"))

This was a little subtle. It was necessary to save the lists as variables, and use the variables in the plot command.

I am not sure what this offers over just having a Python block present in org-mode though. Maybe it is more useful in emacs-lisp libraries where you want to bring in some numerical analysis. Or if you have some custom library of Python you would like to use in elisp. Here is a highly contrived example. Suppose we have a Python module with this special function that converts an argument to "J":

def special_func(x):
    return "J"

In Python, we might use it like this:

import my_python as mp
print [mp.special_func(x) for x in [1, 2, 3]]
['J', 'J', 'J']

We can import the module, and use the function in emacs-lisp too. The underscore in the function name is turned into a dash, which is a little confusing, but it works otherwise.

(pymacs-load "my_python" "mp-")
(mapcar 'mp-special-func '(1 2 3))

It does not seem possible to do everything though. For example, It is not clear how to pass functions through either side. For example, this does not work for fsolve, although it seems like it should.

(pymacs-load "scipy.optimize" "so-")

(defun objective (x)
  (- x 5))

(so-fsolve 'objective 3)

I get an error like this:

Pymacs loading scipy.optimize...done
pymacs-report-error: Python: Emacs: "(wrong-type-argument number-or-marker-p (pymacs-python . 47))"

The Python equivalent is here:

from scipy.optimize import fsolve
def objective(x):
    return x - 5

print fsolve(objective, 3)
[ 5.]

There is an open question on StackOverflow here on this issue. Overall, I find the project very interesting. It would be awesome if you could extend emacs more easily in other languages, especially scripting languages such as Python that have numerical and plotting capabilities. Right now, this is possible in limited ways. For example, Xah Lee describes an approach where an arbitrary script can take data on stdin, process it, and output the results to stdout. Emacs can capture this and use it to modify the buffer. This uses the shell-command features in Emacs. These scripts could be written in Python, Perl, Ruby, etc… This seems like a simpler and more flexible approach, except that it requires creating the shell commands and putting them on the executable path (as opposed to having Python modules on a PYTHONPATH). These lack the deep integration of documentation you get with emacs-lisp and Python functions.

Copyright (C) 2014 by John Kitchin. See the License for information about copying.

org-mode source

Org-mode version = 8.2.7c

Discuss on Twitter