aboutsummaryrefslogtreecommitdiffstats
path: root/lisp/progmodes/python.el
diff options
context:
space:
mode:
authorLele Gaifax2017-11-03 12:20:36 +0000
committerJoão Távora2017-11-05 12:15:45 +0000
commit3ad712ebc91438838df29b0e12b3103d409ee3a4 (patch)
tree9b592f22ea8198dcab0127e4d3058e31102be5d9 /lisp/progmodes/python.el
parent8db2b3a79bef0dab286badc3f0af164387a24a67 (diff)
downloademacs-3ad712ebc91438838df29b0e12b3103d409ee3a4.tar.gz
emacs-3ad712ebc91438838df29b0e12b3103d409ee3a4.zip
Add a Flymake backend for Python (bug#28808)
Implement new Flymake backend with related customizable settings. * lisp/progmodes/python.el (python-flymake-command) (python-flymake-command-output-pattern) (python-flymake-msg-alist): New defcustom. (python--flymake-parse-output): New function, able to parse python-flymake-command output accordingly to python-flymake-command-output-pattern. (python-flymake): New function implementing the backend interface using python--flymake-parse-output for the real work. (python-mode): Add python-flymake to flymake-diagnostic-functions.
Diffstat (limited to 'lisp/progmodes/python.el')
-rw-r--r--lisp/progmodes/python.el136
1 files changed, 135 insertions, 1 deletions
diff --git a/lisp/progmodes/python.el b/lisp/progmodes/python.el
index 895117b9ee3..b7902fb9786 100644
--- a/lisp/progmodes/python.el
+++ b/lisp/progmodes/python.el
@@ -5142,6 +5142,138 @@ returned as is."
5142 (ignore-errors (string-match regexp "") t)) 5142 (ignore-errors (string-match regexp "") t))
5143 5143
5144 5144
5145;;; Flymake integration
5146
5147(defgroup python-flymake nil
5148 "Integration between Python and Flymake."
5149 :group 'python
5150 :link '(custom-group-link :tag "Flymake" flymake)
5151 :version "26.1")
5152
5153(defcustom python-flymake-command '("pyflakes")
5154 "The external tool that will be used to perform the syntax check.
5155This is a non empty list of strings, the checker tool possibly followed by
5156required arguments. Once launched it will receive the Python source to be
5157checked as its standard input.
5158To use `flake8' you would set this to (\"flake8\" \"-\")."
5159 :group 'python-flymake
5160 :type '(repeat string))
5161
5162;; The default regexp accomodates for older pyflakes, which did not
5163;; report the column number, and at the same time it's compatible with
5164;; flake8 output, although it may be redefined to explicitly match the
5165;; TYPE
5166(defcustom python-flymake-command-output-pattern
5167 (list
5168 "^\\(?:<?stdin>?\\):\\(?1:[0-9]+\\):\\(?:\\(?2:[0-9]+\\):\\)? \\(?3:.*\\)$"
5169 1 2 nil 3)
5170 "Specify how to parse the output of `python-flymake-command'.
5171The value has the form (REGEXP LINE COLUMN TYPE MESSAGE): if
5172REGEXP matches, the LINE'th subexpression gives the line number,
5173the COLUMN'th subexpression gives the column number on that line,
5174the TYPE'th subexpression gives the type of the message and the
5175MESSAGE'th gives the message text itself.
5176
5177If COLUMN or TYPE are nil or that index didn't match, that
5178information is not present on the matched line and a default will
5179be used."
5180 :group 'python-flymake
5181 :type '(list regexp
5182 (integer :tag "Line's index")
5183 (choice
5184 (const :tag "No column" nil)
5185 (integer :tag "Column's index"))
5186 (choice
5187 (const :tag "No type" nil)
5188 (integer :tag "Type's index"))
5189 (integer :tag "Message's index")))
5190
5191(defcustom python-flymake-msg-alist
5192 '(("\\(^redefinition\\|.*unused.*\\|used$\\)" . :warning))
5193 "Alist used to associate messages to their types.
5194Each element should be a cons-cell (REGEXP . TYPE), where TYPE must be
5195one defined in the variable `flymake-diagnostic-types-alist'.
5196For example, when using `flake8' a possible configuration could be:
5197
5198 ((\"\\(^redefinition\\|.*unused.*\\|used$\\)\" . :warning)
5199 (\"^E999\" . :error)
5200 (\"^[EW][0-9]+\" . :note))
5201
5202By default messages are considered errors."
5203 :group 'python-flymake
5204 :type `(alist :key-type (regexp)
5205 :value-type (symbol)))
5206
5207(defvar-local python--flymake-proc nil)
5208
5209(defun python--flymake-parse-output (source proc report-fn)
5210 "Collect diagnostics parsing checker tool's output line by line."
5211 (let ((rx (nth 0 python-flymake-command-output-pattern))
5212 (lineidx (nth 1 python-flymake-command-output-pattern))
5213 (colidx (nth 2 python-flymake-command-output-pattern))
5214 (typeidx (nth 3 python-flymake-command-output-pattern))
5215 (msgidx (nth 4 python-flymake-command-output-pattern)))
5216 (with-current-buffer (process-buffer proc)
5217 (goto-char (point-min))
5218 (cl-loop
5219 while (search-forward-regexp rx nil t)
5220 for msg = (match-string msgidx)
5221 for (beg . end) = (flymake-diag-region
5222 source
5223 (string-to-number
5224 (match-string lineidx))
5225 (and colidx
5226 (match-string colidx)
5227 (string-to-number
5228 (match-string colidx))))
5229 for type = (or (and typeidx
5230 (match-string typeidx)
5231 (assoc-default
5232 (match-string typeidx)
5233 python-flymake-msg-alist
5234 #'string-match))
5235 (assoc-default msg
5236 python-flymake-msg-alist
5237 #'string-match)
5238 :error)
5239 collect (flymake-make-diagnostic
5240 source beg end type msg)
5241 into diags
5242 finally (funcall report-fn diags)))))
5243
5244(defun python-flymake (report-fn &rest _args)
5245 "Flymake backend for Python.
5246This backend uses `python-flymake-command' (which see) to launch a process
5247that is passed the current buffer's content via stdin.
5248REPORT-FN is Flymake's callback function."
5249 (unless (executable-find (car python-flymake-command))
5250 (error "Cannot find a suitable checker"))
5251
5252 (when (process-live-p python--flymake-proc)
5253 (kill-process python--flymake-proc))
5254
5255 (let ((source (current-buffer)))
5256 (save-restriction
5257 (widen)
5258 (setq python--flymake-proc
5259 (make-process
5260 :name "python-flymake"
5261 :noquery t
5262 :connection-type 'pipe
5263 :buffer (generate-new-buffer " *python-flymake*")
5264 :command python-flymake-command
5265 :sentinel
5266 (lambda (proc _event)
5267 (when (eq 'exit (process-status proc))
5268 (unwind-protect
5269 (when (with-current-buffer source
5270 (eq proc python--flymake-proc))
5271 (python--flymake-parse-output source proc report-fn))
5272 (kill-buffer (process-buffer proc)))))))
5273 (process-send-region python--flymake-proc (point-min) (point-max))
5274 (process-send-eof python--flymake-proc))))
5275
5276
5145(defun python-electric-pair-string-delimiter () 5277(defun python-electric-pair-string-delimiter ()
5146 (when (and electric-pair-mode 5278 (when (and electric-pair-mode
5147 (memq last-command-event '(?\" ?\')) 5279 (memq last-command-event '(?\" ?\'))
@@ -5255,7 +5387,9 @@ returned as is."
5255 (make-local-variable 'python-shell-internal-buffer) 5387 (make-local-variable 'python-shell-internal-buffer)
5256 5388
5257 (when python-indent-guess-indent-offset 5389 (when python-indent-guess-indent-offset
5258 (python-indent-guess-indent-offset))) 5390 (python-indent-guess-indent-offset))
5391
5392 (add-hook 'flymake-diagnostic-functions #'python-flymake nil t))
5259 5393
5260 5394
5261(provide 'python) 5395(provide 'python)