[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
bug#28808: Consider the right python--flymake-proc
From: |
João Távora |
Subject: |
bug#28808: Consider the right python--flymake-proc |
Date: |
Fri, 03 Nov 2017 12:24:19 +0000 |
User-agent: |
Gnus/5.13 (Gnus v5.13) Emacs/26.0.90 (gnu/linux) |
João Távora <joaotavora@gmail.com> writes:
> Good one.
>
> I'll have a look at all the backends, including yours of course, during the
> next week.
>
> João
>
Sorry for the delay.
I am ready to commit your patch to emacs-26 with very minor changes:
1. The defcustom python-flymake-command-output-regexp is renamed to
python-flymake-command-output-pattern.
2. The docstring of that variable is slightly changed.
3. The function python--flymake is renamed python-flymake, since it is
public to python.el (there was some misunderstanding caused by me
earlier).
4. The commit message is very slightly changed so that the description
sentence starts on a line by its own.
Please verify,
João
>From fd800a9e16493872ff3c8244a2e30e2d9e61fca4 Mon Sep 17 00:00:00 2001
From: Lele Gaifax <lele@metapensiero.it>
Date: Fri, 3 Nov 2017 12:20:36 +0000
Subject: [PATCH] Add a Flymake backend for Python
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.
---
lisp/progmodes/python.el | 136 ++++++++++++++++++++++++++++++++++++++++++++++-
1 file changed, 135 insertions(+), 1 deletion(-)
diff --git a/lisp/progmodes/python.el b/lisp/progmodes/python.el
index 895117b9ee..b7902fb978 100644
--- a/lisp/progmodes/python.el
+++ b/lisp/progmodes/python.el
@@ -5142,6 +5142,138 @@ python-util-valid-regexp-p
(ignore-errors (string-match regexp "") t))
+;;; Flymake integration
+
+(defgroup python-flymake nil
+ "Integration between Python and Flymake."
+ :group 'python
+ :link '(custom-group-link :tag "Flymake" flymake)
+ :version "26.1")
+
+(defcustom python-flymake-command '("pyflakes")
+ "The external tool that will be used to perform the syntax check.
+This is a non empty list of strings, the checker tool possibly followed by
+required arguments. Once launched it will receive the Python source to be
+checked as its standard input.
+To use `flake8' you would set this to (\"flake8\" \"-\")."
+ :group 'python-flymake
+ :type '(repeat string))
+
+;; The default regexp accomodates for older pyflakes, which did not
+;; report the column number, and at the same time it's compatible with
+;; flake8 output, although it may be redefined to explicitly match the
+;; TYPE
+(defcustom python-flymake-command-output-pattern
+ (list
+ "^\\(?:<?stdin>?\\):\\(?1:[0-9]+\\):\\(?:\\(?2:[0-9]+\\):\\)? \\(?3:.*\\)$"
+ 1 2 nil 3)
+ "Specify how to parse the output of `python-flymake-command'.
+The value has the form (REGEXP LINE COLUMN TYPE MESSAGE): if
+REGEXP matches, the LINE'th subexpression gives the line number,
+the COLUMN'th subexpression gives the column number on that line,
+the TYPE'th subexpression gives the type of the message and the
+MESSAGE'th gives the message text itself.
+
+If COLUMN or TYPE are nil or that index didn't match, that
+information is not present on the matched line and a default will
+be used."
+ :group 'python-flymake
+ :type '(list regexp
+ (integer :tag "Line's index")
+ (choice
+ (const :tag "No column" nil)
+ (integer :tag "Column's index"))
+ (choice
+ (const :tag "No type" nil)
+ (integer :tag "Type's index"))
+ (integer :tag "Message's index")))
+
+(defcustom python-flymake-msg-alist
+ '(("\\(^redefinition\\|.*unused.*\\|used$\\)" . :warning))
+ "Alist used to associate messages to their types.
+Each element should be a cons-cell (REGEXP . TYPE), where TYPE must be
+one defined in the variable `flymake-diagnostic-types-alist'.
+For example, when using `flake8' a possible configuration could be:
+
+ ((\"\\(^redefinition\\|.*unused.*\\|used$\\)\" . :warning)
+ (\"^E999\" . :error)
+ (\"^[EW][0-9]+\" . :note))
+
+By default messages are considered errors."
+ :group 'python-flymake
+ :type `(alist :key-type (regexp)
+ :value-type (symbol)))
+
+(defvar-local python--flymake-proc nil)
+
+(defun python--flymake-parse-output (source proc report-fn)
+ "Collect diagnostics parsing checker tool's output line by line."
+ (let ((rx (nth 0 python-flymake-command-output-pattern))
+ (lineidx (nth 1 python-flymake-command-output-pattern))
+ (colidx (nth 2 python-flymake-command-output-pattern))
+ (typeidx (nth 3 python-flymake-command-output-pattern))
+ (msgidx (nth 4 python-flymake-command-output-pattern)))
+ (with-current-buffer (process-buffer proc)
+ (goto-char (point-min))
+ (cl-loop
+ while (search-forward-regexp rx nil t)
+ for msg = (match-string msgidx)
+ for (beg . end) = (flymake-diag-region
+ source
+ (string-to-number
+ (match-string lineidx))
+ (and colidx
+ (match-string colidx)
+ (string-to-number
+ (match-string colidx))))
+ for type = (or (and typeidx
+ (match-string typeidx)
+ (assoc-default
+ (match-string typeidx)
+ python-flymake-msg-alist
+ #'string-match))
+ (assoc-default msg
+ python-flymake-msg-alist
+ #'string-match)
+ :error)
+ collect (flymake-make-diagnostic
+ source beg end type msg)
+ into diags
+ finally (funcall report-fn diags)))))
+
+(defun python-flymake (report-fn &rest _args)
+ "Flymake backend for Python.
+This backend uses `python-flymake-command' (which see) to launch a process
+that is passed the current buffer's content via stdin.
+REPORT-FN is Flymake's callback function."
+ (unless (executable-find (car python-flymake-command))
+ (error "Cannot find a suitable checker"))
+
+ (when (process-live-p python--flymake-proc)
+ (kill-process python--flymake-proc))
+
+ (let ((source (current-buffer)))
+ (save-restriction
+ (widen)
+ (setq python--flymake-proc
+ (make-process
+ :name "python-flymake"
+ :noquery t
+ :connection-type 'pipe
+ :buffer (generate-new-buffer " *python-flymake*")
+ :command python-flymake-command
+ :sentinel
+ (lambda (proc _event)
+ (when (eq 'exit (process-status proc))
+ (unwind-protect
+ (when (with-current-buffer source
+ (eq proc python--flymake-proc))
+ (python--flymake-parse-output source proc report-fn))
+ (kill-buffer (process-buffer proc)))))))
+ (process-send-region python--flymake-proc (point-min) (point-max))
+ (process-send-eof python--flymake-proc))))
+
+
(defun python-electric-pair-string-delimiter ()
(when (and electric-pair-mode
(memq last-command-event '(?\" ?\'))
@@ -5255,7 +5387,9 @@ python-mode
(make-local-variable 'python-shell-internal-buffer)
(when python-indent-guess-indent-offset
- (python-indent-guess-indent-offset)))
+ (python-indent-guess-indent-offset))
+
+ (add-hook 'flymake-diagnostic-functions #'python-flymake nil t))
(provide 'python)
--
2.14.2
- bug#28808: Consider the right python--flymake-proc,
João Távora <=