diff options
| author | Lele Gaifax | 2017-11-03 12:20:36 +0000 |
|---|---|---|
| committer | João Távora | 2017-11-05 12:15:45 +0000 |
| commit | 3ad712ebc91438838df29b0e12b3103d409ee3a4 (patch) | |
| tree | 9b592f22ea8198dcab0127e4d3058e31102be5d9 /lisp/progmodes/python.el | |
| parent | 8db2b3a79bef0dab286badc3f0af164387a24a67 (diff) | |
| download | emacs-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.el | 136 |
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. | ||
| 5155 | This is a non empty list of strings, the checker tool possibly followed by | ||
| 5156 | required arguments. Once launched it will receive the Python source to be | ||
| 5157 | checked as its standard input. | ||
| 5158 | To 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'. | ||
| 5171 | The value has the form (REGEXP LINE COLUMN TYPE MESSAGE): if | ||
| 5172 | REGEXP matches, the LINE'th subexpression gives the line number, | ||
| 5173 | the COLUMN'th subexpression gives the column number on that line, | ||
| 5174 | the TYPE'th subexpression gives the type of the message and the | ||
| 5175 | MESSAGE'th gives the message text itself. | ||
| 5176 | |||
| 5177 | If COLUMN or TYPE are nil or that index didn't match, that | ||
| 5178 | information is not present on the matched line and a default will | ||
| 5179 | be 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. | ||
| 5194 | Each element should be a cons-cell (REGEXP . TYPE), where TYPE must be | ||
| 5195 | one defined in the variable `flymake-diagnostic-types-alist'. | ||
| 5196 | For example, when using `flake8' a possible configuration could be: | ||
| 5197 | |||
| 5198 | ((\"\\(^redefinition\\|.*unused.*\\|used$\\)\" . :warning) | ||
| 5199 | (\"^E999\" . :error) | ||
| 5200 | (\"^[EW][0-9]+\" . :note)) | ||
| 5201 | |||
| 5202 | By 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. | ||
| 5246 | This backend uses `python-flymake-command' (which see) to launch a process | ||
| 5247 | that is passed the current buffer's content via stdin. | ||
| 5248 | REPORT-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) |