Vinod Kurup

Hospitalist/programmer in search of the meaning of life

Emacs for Python Programming

(Note: See the bottom of this post for updates)

I was honored to to give my “Emacs for Python Programming” talk at the inaugural PyCarolinas conference. The conference was a huge success in every way, thanks to the efforts of Calvin Spealman, et. al. I promised that I would post my Emacs setup, so here it is.

Step 0: Prerequisites

This may work with other setups, but here’s what I tested:

Step 1: Clear out any previous customization

As evidenced by the numerous blog posts documenting how to set this up, it’s not straightforward. I recommend starting from scratch to make sure everything is set up properly. Once you have it working, then you can customize it. Make sure to start these commands in your home directory.

1
2
vinod:~$ mkdir old-emacs
vinod:~$ mv .emacs .emacs.d old-emacs/

Step 2: Set up initial Emacs configuration

Create an empty .emacs.d directory and a subdirectory named with your username. My username is vinod, so we’ll create a directory structure called .emacs.d/vinod/. The emacs-starter-kit tells emacs to automatically load any elisp files from that special directory, so you’ll always have a place to drop custom elisp files.

1
vinod:~$ mkdir -p ~/.emacs.d/$USER

Create a file named .emacs.d/init.el with the following contents:

my-init.ellink
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
(require 'package)
(add-to-list 'package-archives '("marmalade" . "http://marmalade-repo.org/packages/"))
(add-to-list 'package-archives '("melpa" . "http://melpa.milkbox.net/packages/"))
(package-initialize)

(when (not package-archive-contents)
  (package-refresh-contents))

(defvar my-packages '(starter-kit
                      starter-kit-bindings
                      starter-kit-js
                      autopair
                      yasnippet
                      auto-complete
                      fuzzy)
  "A list of packages to ensure are installed at launch.")

(dolist (p my-packages)
  (when (not (package-installed-p p))
    (package-install p)))

;; autopair and yas in all modes
(autopair-global-mode)
(yas-global-mode 1)

;; autocomplete
(require 'auto-complete-config)
(setq ac-dictionary-files (list (concat user-emacs-directory ".dict")))
(ac-config-default)
;; hack to fix ac-sources after pycomplete.el breaks it
(add-hook 'python-mode-hook
          '(lambda ()
             (setq ac-sources '(ac-source-pycomplete
                                ac-source-abbrev
                                ac-source-dictionary
                                ac-source-words-in-same-mode-buffers))))

;; Set up python-mode
(setq py-install-directory (concat esk-user-dir "/python-mode.el-6.0.12/"))
(add-to-list 'load-path py-install-directory)
;; this will show method signatures while typing
(setq py-set-complete-keymap-p t)
(require 'python-mode)
;; activate the virtualenv where Pymacs is located
(virtualenv-workon "default/")

(defun load-pycomplete ()
  "Load and initialize pycomplete."
  (interactive)
  (let* ((pyshell (py-choose-shell))
         (path (getenv "PYTHONPATH")))
    (setenv "PYTHONPATH" (concat
                          (expand-file-name py-install-directory) "completion"
                          (if path (concat path-separator path))))
    (if (py-install-directory-check)
        (progn
          (setenv "PYMACS_PYTHON" (if (string-match "IP" pyshell)
                                      "python"
                                    pyshell))
          (autoload 'pymacs-apply "pymacs")
          (autoload 'pymacs-call "pymacs")
          (autoload 'pymacs-eval "pymacs")
          (autoload 'pymacs-exec "pymacs")
          (autoload 'pymacs-load "pymacs")
          (load (concat py-install-directory "completion/pycomplete.el") nil t)
          (add-hook 'python-mode-hook 'py-complete-initialize))
      (error "`py-install-directory' not set, see INSTALL"))))
(eval-after-load 'pymacs '(load-pycomplete))

;; pyflakes flymake integration
;; http://stackoverflow.com/a/1257306/347942
(when (load "flymake" t)
  (defun flymake-pyflakes-init ()
    (let* ((temp-file (flymake-init-create-temp-buffer-copy
                       'flymake-create-temp-inplace))
           (local-file (file-relative-name
                        temp-file
                        (file-name-directory buffer-file-name))))
      (list "pycheckers" (list local-file))))
  (add-to-list 'flymake-allowed-file-name-masks
               '("\\.py\\'" flymake-pyflakes-init)))
(add-hook 'python-mode-hook 'flymake-mode)

;; menu bar is useful when getting started
(menu-bar-mode)
(setq-default default-tab-width 4)

Step 3: Create a python virtualenv

We’ll keep all the python-side customization in a virtualenv named default. It’s important that the name you choose is the same as the name in the (virtualenv-workon) command in line 45 of Step 2.

1
vinod:~$ mkvirtualenv -p python2 default

After it does its thing, your shell prompt should change, indicating that your new virtualenv is activated

1
(default)vinod:~$

Step 4: Install Pymacs

Pymacs is a really cool piece of software that sets up a 2 way communication between Emacs and Python, allowing you to control Emacs with python commands rather than elisp commands. It requires a python piece (Pymacs.py) and an Emacs piece (pymacs.el). Unfortunately, they’re not installable via pip or package.el, but installation is easy enough.

1
2
3
4
5
6
7
8
9
10
11
12
13
(default)vinod:~$ mkdir src
(default)vinod:~/src$ cd src
(default)vinod:~/src$ git clone git://github.com/pinard/Pymacs.git
(default)vinod:~/src$ cd Pymacs

# check to make sure tests pass
(default)vinod:~/src/Pymacs$ make check

# install it (Be sure you're inside your virtualenv!)
(default)vinod:~/src/Pymacs$ make install

# install the emacs extension
(default)vinod:~/src/Pymacs$ cp pymacs.el ~/.emacs.d/$USER/

Step 5: Install other python packages

These helper packages are easier to install:

1
(default)vinod:~$ pip install pyflakes pep8

The pyflakes and pep8 packages check your code as you type using Emacs’ flymake mode. Now, deactivate your virtualenv.

1
2
(default)vinod:~$ deactivate
vinod:~$

Step 6: Install pycheckers

Flymake is the part of Emacs that checks your code for errors on the fly. It calls a shell script called pycheckers, so you need to have a script by that name in your shell’s PATH. Here’s mine (~/bin/pycheckers):

pycheckers.shlink
1
2
3
4
5
#!/bin/bash

pyflakes "$1"
pep8 --repeat "$1"
true

Step 7: Install python-mode.el

As I mentioned in my talk, there are multiple Python modes available, but I recommend using the one named python-mode.el, which is developed at http://launchpad.net/python-mode. It does periodically get uploaded to Marmalade, but the auto-completion using pycomplete doesn’t work well on the version that is there now (6.0.10), so I recommend that you manually download and install the latest stable version (6.0.12). I’ll update this post once a stable working version gets uploaded to Marmalade or Melpa.

1
2
vinod:~$ cd ~/.emacs.d/$USER
vinod:~/.emacs.d/vinod$ curl -L https://launchpad.net/python-mode/trunk/6.0.12/+download/python-mode.el-6.0.12.tar.gz | tar xz

Step 8: Test it all out

Launch emacs and open a python file named test.py. Type the following:

import os
os.path.jo

Wait at this point. You should see auto-completion of os.path.join followed shortly by a yellow popup showing documentation of that method. Hitting return should accept the completion. Then type ( and you should see the method signature in the minibuffer.

Move the cursor over any letter in join. Hit ‘F1’. A window should popup with the docstring for os.path.join. Hit ‘F2’. A new window should be opened with the code for os.path.join. Hit ‘F3’. You’ll be prompted to enter the name of any python command, and Emacs will show you the docstring.

Type the string ‘blah’ and hit return. The string should be highlighted in pink and if you mouseover it, the minibuffer will say undefined name 'blah'. That’s flymake working for you.

Hit C-c C-c and the buffer should be sent to a Python interpreter and you’ll be dropped in the REPL after the code has been loaded. Any errors in the code will be reported.

If all of this works, then CONGRATULATIONS!!! If not, let me know and I’ll see if I can help debug.

Step 9: Customize

All of this is customizable in hundreds of ways. Try M-x customize-group RET python-mode RET to see how.

Step 10: How to create a new python project

Whenever you want to create a new Python project, you have do the following:

Create a new virtualenv

1
vinod:~$ mkvirtualenv newproject

Install pymacs into that virtualenv

1
2
(newproject)vinod:~$ cd ~/src/Pymacs
(newproject)vinod:~/src/Pymacs$ make install

Install the other pip modules

1
(newproject)vinod:~/src/Pymacs$ pip install pyflakes pep8

Switch to that virtualenv in emacs

M-x virtualenv-workon RET newproject/ RET

Issues

Changing virtualenvs doesn’t restart Pymacs

For the most part, this won’t affect much, but if you move from a Python2 project to a Python3 project, you may have problems. The workaround is to call M-x pymacs-terminate-services, then M-x virtualenv-workon to change your virtualenv, and then finally M-x load-pycomplete to restart pymacs and pycomplete.

Latest stable version of python-mode.el isn’t on Melpa

I’d much prefer to just load python-mode using the built-in Emacs package manager.

Pymacs installation is harder than it needs to be

I wish Pymacs could be installed by a 2 step process

  1. pip install Pymacs
  2. M-x package-install RET pymacs RET

Which pycheckers to use?

My script uses pep8 and pyflakes. There are a lot of other options out there and I do not know which is best. See this post for more details.

Not using ropemacs any more

In my talk, I used ropemacs. In this setup, I have decided against it. I was mostly using it for code-completion and documentation lookup, but I like pycomplete.el better for those purposes. It’s included in python-mode and it shows method signatures as you type. Ropemacs does have other features such as project management (easily jumping to files in the same project), and refactoring (changing variable names throughout project). I haven’t used those, so I decided not to install ropemacs.

Project management not included

I haven’t yet decided which package to use for this. I’ll probably try projectile, but there are many other options, some lightweight (ropemacs, eproject, pony-mode) and some heavyweight (ecb, CEDET).

References

I gleaned knowledge from all of the following, in no particular order.

Updates

2012-10-27

Thanks to @danpoirier and @gregnewman for testing. I initially recommended using the master branch of python-mode.el, but now I recommend using the latest stable release (as of now) which is version 6.0.12 to avoid problems loading Pymacs. There’s a bug on 6.0.12 with virtualenv-workon that requires you to put a trailing slash after the name of your virtualenv, so I’ve updated the init.el file above to account for those changes.

Comments