Skip to content

Instantly share code, notes, and snippets.

@UnwashedMeme
Last active December 17, 2015 14:09
Show Gist options
  • Save UnwashedMeme/5622618 to your computer and use it in GitHub Desktop.
Save UnwashedMeme/5622618 to your computer and use it in GitHub Desktop.
flymake-python-pyflakes customization

flymake-python-pyflakes

What will we get

Python buffers (that aren't tramp remote) will be highlighted by pyflakes and pep8. They have 'questionable' lines highlighted and error lines significantly highlighted. When you move the cursor into that section it should print the error message in the minibuffer; if there are two errors it will only print the first.

You can use the keychord M-n and M-p to go to the next and previous error section; these are bound to flymake-goto-{next,prev}-error.

You can use the keychord C-c i to ignore the error code on the current line; M-x my-pyflakes-reset-ignore will reset to ignoring none.

You can run the function M-x mypylint to run the pylint version of the code analysis. This tends to be similar but has slightly different conditions. In the resulting buffer i will ignore a message and M-x mypylint-reset-ignore will reset. The two lists are distinct since the error messages are distinct.

Emacs

I'm running emacs 24.3.50.1 which can be gotten with emacs-snapshot from:

sudo add-apt-repository ppa:cassou/emacs; 
sudo aptitude update; 
sudo aptitude install emacs-snapshot

This will all probably work with emacs 24.1, but I don't know. I've been running this emacs-snapshot branch for quite some time and can't recall being bitten by instability.

flake8

flake8 is the command that actually checks the code (pep8 + other)

Install the python packages sudo pip install flake8 pylint

Install the emacs packages M-x package-list-packages

Select

  • python-pylint 1.1
  • flymake-python-pyflakes 0.8

and install (hit i).

custom el

By default flymake will highlight lines that are erring, but does not make it obvious what the error is. You have to do things like M-x flymake-goto-next-error and M-x flymake-display-err-menu-for-current-line.

Our solution is to define a small minor mode to make this easier.

Add mypylint.el to your site-lisp folder (i.e. in your load-path somewhere) and add the following snippet to your emacs init.el (or appropriate settings file).

;;;; pyflakes
(autoload 'mypylint "mypylint")
(autoload 'my-flymake-minor-mode "mypylint")
(defun maybe-flymake-activate ()
  (cond ((not (tramp-handle-file-remote-p (buffer-file-name)))
         (message "Activating flymake-python-pyflakes")
         (flymake-python-pyflakes-load)
         (message "Activing custom navigation minor-mode.")
         (my-flymake-minor-mode))
        (t
         (message "Skipping flymake on remote tramp buffer."))))

;;; maybe start pyflakes when we load python
(defvar mypylint-history nil "History list for pylint")
(defvar mypylint-ignore-message-ids nil "List of pylint message ids to ignore.")
(defun mypylint-reset-ignore ()
"Reset the list of ignored pep8/pylint error codes to none."
(interactive)
(setf mypylint-ignore-message-ids nil))
(defun mypylint-ignore-message (message-id)
(interactive
(list
(if (string= (buffer-name (current-buffer)) "*python-pylint*")
(let ((case-fold-search nil)
(line (thing-at-point 'line)))
(message line)
(if (string-match "\\[\\([CWRE][0-9]+\\)" line)
(match-string 1 line)
(read-from-minibuffer "Message ID: ")))
)))
(cond
(message-id
(pushnew message-id mypylint-ignore-message-ids :test 'string=)
(message "Currently ignoring: %s" mypylint-ignore-message-ids))
(t (message "No message-id found to ignore."))))
(eval-after-load 'python-pylint
'(define-key python-pylint-mode-map "i" 'mypylint-ignore-message))
(defun mypylint (command)
(interactive
(progn
(require 'python-pylint)
(let* ((tramp (tramp-tramp-file-p (buffer-file-name)))
(file (or (and tramp
(aref (tramp-dissect-file-name (buffer-file-name)) 3))
(file-relative-name (buffer-file-name))))
(python-pylint-options
(if mypylint-ignore-message-ids
(list* "-d" (mapconcat 'identity mypylint-ignore-message-ids ",")
python-pylint-options)
python-pylint-options))
(command (mapconcat
'identity
(list python-pylint-command
(mapconcat 'identity python-pylint-options " ")
(comint-quote-filename file)) " ")))
(list (if current-prefix-arg
(read-from-minibuffer "Run pylint: " command
nil nil 'mypylint-history)
command)))))
(save-some-buffers (not python-pylint-ask-about-save) nil)
(message "Running pylint command %s" command)
(compilation-start command 'python-pylint-mode))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;; flymake-python-pyflakes minor mode for easing navigation.
(setq flymake-python-pyflakes-executable "flake8")
(defvar my-pyflakes-ignore-message-ids nil
"List of flake8 message ids to ignore.")
;;; Define a minor mode to help navigate around
;;; Cribbed from http://www.emacswiki.org/emacs/FlyMake
(defvar my-pyflakes-minor-mode-map
(let ((map (make-sparse-keymap)))
(define-key map "\M-p" 'flymake-goto-prev-error)
(define-key map "\M-n" 'flymake-goto-next-error)
(define-key map (kbd "C-c i") 'my-pyflakes-ignore-error)
map)
"Keymap for my flymake minor mode.")
(define-minor-mode my-pyflakes-minor-mode
"Simple minor mode which adds some key bindings for moving to the next and previous errors.
Key bindings:
\\{my-pyflakes-minor-mode-map}"
nil
nil
:keymap my-pyflakes-minor-mode-map)
(defun my-pyflakes-get-help ()
"Find the flymake help text for the current point"
(when (get-char-property (point) 'flymake-overlay)
(let ((help (get-char-property (point) 'help-echo)))
help)))
(defun my-pyflakes-show-help ()
"Print in the minibuffer the help text for highlighted error at the current point."
(let ((help (my-pyflakes-get-help)))
(when help (message "%s" help))))
;;; run the show-help function after each command so that is is always up to date.
(add-hook 'post-command-hook 'my-pyflakes-show-help)
(defun my-pyflakes-ignore-error ()
"Ignore the error code that is highlighting the current point"
(interactive)
(let ((help (my-pyflakes-get-help)))
(message "help was: %s" help)
(when (and help (string-match "^\\([CWRE][0-9]+\\)" help))
(let ((message-id (match-string 1 help)))
(pushnew message-id my-pyflakes-ignore-message-ids :test 'string=)
(my-pyflakes-build-ignore)
(message "Ignored: %s" message-id)
))))
(defun my-pyflakes-build-ignore ()
"Build the `--ignore=... argument out of our ignored codes.`"
(setq flymake-python-pyflakes-extra-arguments
(when my-pyflakes-ignore-message-ids
(list (concat "--ignore=" (mapconcat 'identity my-pyflakes-ignore-message-ids ",")))))
(when flymake-mode
;; in case this is triggered by mypylint on a buffer not running flymake.
(flymake-start-syntax-check)))
(defun my-pyflakes-reset-ignore ()
(interactive)
(setq my-pyflakes-ignore-message-ids nil)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment