Skip to content.

plope

Personal tools
You are here: Home » Members » chrism's Home » Flymake Mode for Emacs / Python
 
 

Flymake Mode for Emacs / Python

How I cargo-cult-configured the useful Flymake Emacs minor mode to use pyflakes.

I found something useful! I found something useful!

Flymake is an on-the-fly syntax checker for Emacs. Essentially every so often it runs your buffer through a language-specific syntax checker and highlights code errors and warnings in a different color. I can hear "real" IDE people laughing at me now, but for better or worse, I will probably use Emacs until I die; not because I like it particularly, but because it's the only thing my fingers know how to use, thus searching for better tools is pointless.

Out of the box, Flymake can't analyze Python code, but it helpfully has a plugin system. It allows you to plug in, for example, pylint or pyflakes or probably any other checker. I use pyflakes regularly. Pyflakes is excellent, it basically just whines when you're using a name that hasn't been defined (an indictment of your lack of testing for that code path) or you've imported a name twice, or you're not using a name that you've imported. Importantly, it walks the Python AST rather than trying to import things (which other checkers seem to tend to try to do), so it's fast and using it has no side effects. I tend to use pyflakes as a before-checkin sanity check to make sure I haven't forgotten to remove an import, so it made sense to try to fit it into my editor.

Here's what I did to get it working. As I said, it's all cargo-culted, consistent with the rest of my Emacs configuration.

  • I downloaded flymake.el and put it in a directory in my emacs load-path. I'm not even sure I needed to do this, I think it ships with Emacs now. Who knows, it works.
  • I made sure I could invoke pyflakes as "pyflakes" (I put the pyflakes executable on my PATH).
  • I added some (more) inscrutable stuff to my .emacs file

The stuff I added to the .emacs file:

  (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 "pyflakes" (list local-file)))) 

         (add-to-list 'flymake-allowed-file-name-masks 
                  '("\\.py\\'" flymake-pyflakes-init))) 

   (add-hook 'find-file-hook 'flymake-find-file-hook)

The first stanza tells flymake to use pyflakes on .py files when in flymake-mode. The second tells emacs to always use flymake as a minor mode when loading buffers so I don't have to type "M-x flymake-mode" to get syntax checking when I load a buffer.

And now! It works. I don't know how it works but it does. The below picture proves it (I import traceback but I don't use it).

And thus verify come the inane checkins for things I'm working on now that read "remove unused import".

Created by chrism
Last modified 2008-02-05 04:11 PM

attempt...

I tried this out and kind of got it working; I couldn't get your exact code to work, so I just ended up patching flymake.el directly, at:
https://svn.openplans.org/svn/dotemacs/flymake.el

Works pretty well, though it seems like it tries to check the file too often. Maybe there's a setting for that. Except it doesn't handle syntax errors well, and since it checks as you edit the file often the file is not syntactically correct (e.g., you are half-way through entering a line). When there's a syntax error I get an elisp error "Error in process filter: Wrong type argument: stringp, 1". I don't know what that's about, though the pyflakes output for syntax errors is different enough that it might be a problem. Shouldn't be an exception, though. I tried adding another regex for that too, but no luck (I'm not confident about my emacs regexps though).

neat!

Thanks for finding this. One tweak that's useful to those of us with dark background / light foreground... add to your custom-set-faces section of .emacs:

'(flymake-errline ((((class color)) (:background "LightPink" :foreground "black"))))
'(flymake-warnline ((((class color)) (:background "LightBlue2" :foreground "black"))))


I don't seem to be having the problems Ian's having, though so far I've only toyed with it. We'll see how it goes in real usage when I get back from vacation in a week. FWIW, I'm using latest flymake from cvs trunk (r1.2.4.30) and pyflakes v. 0.2.1.

python syntax errors

fwiw Ian, what I get when I have a python syntax error is this: pyflakes highlights the first line of the file. I don't see any elisp errors or warnings.
And I just copied Chris' .emacs additions verbatim, I didn't use your customized version of pyflakes. I'm using GNU Emacs 22.1.1.

It does check pretty darn often... about once a second AFAICT. Would be nice to dial that down.

errors in minibuffer...

Jeroen Vloothuis sent me this in email, which is really useful

"""
If you like flymake but don't like using the mouse to see the error info try this extension (written by a colleague of mine, Rohan Nicholls). Chuck it in your load path and add something like the example below to your .emacs:

(load-library "flymake-cursor")

Errors will no be displayed in the minibuffer.

;; -*- emacs-lisp -*-
;; License: Gnu Public License
;;
;; Additional functionality that makes flymake error messages appear
;; in the minibuffer when point is on a line containing a flymake
;; error. This saves having to mouse over the error, which is a
;; keyboard user's annoyance

;;flymake-ler(file line type text &optional full-file)
(defun show-fly-err-at-point ()
"If the cursor is sitting on a flymake error, display the
message in the minibuffer"
(interactive)
(let ((line-no (line-number-at-pos)))
(dolist (elem flymake-err-info)
(if (eq (car elem) line-no)
(let ((err (car (second elem))))
(message "%s" (fly-pyflake-determine-message err)))))))

(defun fly-pyflake-determine-message (err)
"pyflake is flakey if it has compile problems, this adjusts the
message to display, so there is one ;)"
(cond ((not (or (eq major-mode 'Python) (eq major-mode 'python-mode) t)))
((null (flymake-ler-file err))
;; normal message do your thing
(flymake-ler-text err))
(t ;; could not compile err
(format "compile error, problem on line %s" (flymake-ler-line err)))))

(defadvice flymake-goto-next-error (after display-message activate compile)
"Display the error in the mini-buffer rather than having to mouse over it"
(show-fly-err-at-point))

(defadvice flymake-goto-prev-error (after display-message activate compile)
"Display the error in the mini-buffer rather than having to mouse over it"
(show-fly-err-at-point))

(defadvice flymake-mode (before post-command-stuff activate compile)
"Add functionality to the post command hook so that if the
cursor is sitting on a flymake error the error information is
displayed in the minibuffer (rather than having to mouse over
it)"
(set (make-local-variable 'post-command-hook)
(cons 'show-fly-err-at-point post-command-hook)))
"""

non .py files?

This is working great on my ubuntu setup. I couldn't figure out how to start flymaking on non *.py files (toplevel programs). Just 'flymake-mode' didn't start pyflakes. 'flymake-pyflakes-init' was not a command I could run.