emacs-elpa-diffs
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[nongnu] elpa/flymake-collection 74e2f3fb4b 54/89: Set the working dir a


From: ELPA Syncer
Subject: [nongnu] elpa/flymake-collection 74e2f3fb4b 54/89: Set the working dir and shadow file flag for mypy (#9)
Date: Mon, 6 Jan 2025 19:00:10 -0500 (EST)

branch: elpa/flymake-collection
commit 74e2f3fb4b9b4944f1c90377d7d2f16e57d6d5cd
Author: dmitrig <34632025+dmitrig@users.noreply.github.com>
Commit: GitHub <noreply@github.com>

    Set the working dir and shadow file flag for mypy (#9)
    
    * Run mypy from inferred python project root
    
    * Remove leading dot from temp file name
    
    This appears to break relative imports in files checked by mypy. Maybe
    make the temp file pattern configurable per checker?
    
    * Use mypy --shadow-file for buffers with a file
    
    Fall back to the buffer name when `buffer-file-name' is nil. Necessary
    for checking temp files since python module names are based on the
    file name.
    
    * Parse mypy diagnostic ids
    
    * Make the mypy working dir configurable
    
    * Add basic tests for mypy
    
    * Add require for project
    
    * Move required mypy flags out of custom variable
    
    * Fix parsing of mypy error ids with hyphens
    
    * Add temp-file-prefix paramter
    
    * Update src/checkers/flymake-collection-mypy.el
    
    Co-authored-by: dmitrig <34632025+dgarbuzov@users.noreply.github.com>
    Co-authored-by: Mohsin Kaleem <mohkale@kisara.moe>
---
 src/checkers/flymake-collection-mypy.el | 93 ++++++++++++++++++++++++++++-----
 src/flymake-collection-define.el        | 24 ++++++---
 tests/checkers/installers/mypy.bash     |  1 +
 tests/checkers/test-cases/mypy.yml      | 47 +++++++++++++++++
 4 files changed, 144 insertions(+), 21 deletions(-)

diff --git a/src/checkers/flymake-collection-mypy.el 
b/src/checkers/flymake-collection-mypy.el
index 1fa8f27444..616d5659b1 100644
--- a/src/checkers/flymake-collection-mypy.el
+++ b/src/checkers/flymake-collection-mypy.el
@@ -26,34 +26,101 @@
 
 ;;; Code:
 
+(require 'project)
 (require 'flymake)
 (require 'flymake-collection)
 
 (eval-when-compile
   (require 'flymake-collection-define))
 
+(defcustom flymake-collection-mypy-args
+  '("--show-error-codes")
+  "Command line arguments always passed to `flymake-collection-mypy'."
+  :type '(repeat (string :tag "Arg"))
+  :group 'flymake-collection)
+
+(defcustom flymake-collection-mypy-project-root
+  '(("mypy.ini" "project.toml" "setup.cfg") project-root default-directory)
+  "Method to set project root."
+  :type '(repeat :tag "Run mypy from the first choice that succeeds"
+                 (choice (const :tag "The buffer default-directory" 
default-directory)
+                         (const :tag "The current project root" project-root)
+                         (directory :tag "A specific directory")
+                         (repeat :tag "The first ancestor directory containing"
+                                 :value ("mypy.ini" "pyproject.toml" 
"setup.cfg")
+                                 (string :tag "File name"))))
+  :group 'flymake-collection
+  :safe 'listp)
+
+(defun flymake-collection-mypy--locate-dominating-files (buffer files)
+  "Find ancestor directory of `BUFFER' containing any of `FILES'."
+  (let* ((start (if-let ((file (buffer-file-name buffer)))
+                    (file-name-directory file)
+                  (buffer-local-value 'default-directory buffer)))
+         (regex (mapconcat 'regexp-quote files "\\|"))
+         (root (locate-dominating-file
+                start (lambda (dir) (directory-files dir nil regex t)))))
+    root))
+
+(defun flymake-collection-mypy--default-directory (buffer)
+  "Find a directory from which to run mypy to check `BUFFER'.
+Try each method specified in `flymake-collection-mypy-project-root' in
+order and returns the first non-nil result."
+  (cl-dolist (spec flymake-collection-mypy-project-root)
+    (when-let
+        ((res (cond
+               ((eq spec 'default-directory)
+                (buffer-local-value 'default-directory buffer))
+               ((eq spec 'project-root)
+                (when-let ((proj (project-current)))
+                  (project-root proj)))
+               ((listp spec)
+                (flymake-collection-mypy--locate-dominating-files
+                 buffer spec))
+               ((stringp spec) spec))))
+      (cl-return res))))
+
+
 ;;;###autoload (autoload 'flymake-collection-mypy "flymake-collection-mypy")
 (flymake-collection-define-rx flymake-collection-mypy
   "Mypy syntax and type checker.  Requires mypy>=0.580.
 
 See URL `http://mypy-lang.org/'."
   :title "mypy"
-  :pre-let ((mypy-exec (executable-find "mypy")))
-  :pre-check (unless mypy-exec
-               (error "Cannot find mypy executable"))
+  :pre-let ((mypy-exec (executable-find "mypy"))
+            (default-directory (flymake-collection-mypy--default-directory
+                                flymake-collection-source)))
+  :pre-check (progn
+               (flymake-log :debug "Working dir is %s" default-directory)
+               (unless mypy-exec
+                 (error "Cannot find mypy executable"))
+               (unless default-directory
+                 (error "Default dir is nil: check 
`flymake-collection-mypy-project-root'")))
   :write-type 'file
   :source-inplace t
-  :command (list mypy-exec
-                 "--show-column-numbers"
-                 "--no-error-summary"
-                 "--no-color-output"
-                 "--show-absolute-path"
-                 "--show-error-codes"
-                 flymake-collection-temp-file)
+  :command `(,mypy-exec
+             "--show-column-numbers"
+             "--show-absolute-path"
+             "--no-error-summary"
+             "--no-color-output"
+             ,@flymake-collection-mypy-args
+             ,@(if-let ((source-file (buffer-file-name
+                                      flymake-collection-source))
+                        ((file-exists-p source-file)))
+                   (list
+                    "--shadow-file" source-file flymake-collection-temp-file
+                    source-file)
+                 (list flymake-collection-temp-file)))
+  ;; Temporary file cannot start with a dot for mypy, see
+  ;; https://github.com/mohkale/flymake-collection/pull/9
+  :temp-file-prefix "flymake_mypy_"
   :regexps
-  ((error   bol (file-name) ":" line ":" column ": error: "   (message) eol)
-   (warning bol (file-name) ":" line ":" column ": warning: " (message) eol)
-   (note    bol (file-name) ":" line ":" column ": note: "    (message) eol)))
+  ((error   bol (file-name) ":" line ":" column ": error: "
+            (message) (opt "  [" (id (* graph)) "]") eol)
+   (warning bol (file-name) ":" line ":" column ": warning: "
+            (message) (opt "  [" (id (* graph)) "]") eol)
+   (note    bol (file-name) ":" line ":" column ": note: "
+            (message) (opt "  [" (id (* graph)) "]") eol)))
 
 (provide 'flymake-collection-mypy)
 
diff --git a/src/flymake-collection-define.el b/src/flymake-collection-define.el
index fe7c706e67..7c32039034 100644
--- a/src/flymake-collection-define.el
+++ b/src/flymake-collection-define.el
@@ -60,11 +60,13 @@ killed and replaced with the new check.")
 
 (define-obsolete-function-alias 'flymake-rest-define 
'flymake-collection-define "2.0.0")
 
-(defun flymake-collection-define--temp-file (temp-dir temp-file source-inplace)
+(defun flymake-collection-define--temp-file
+    (temp-dir temp-file source-inplace temp-file-prefix)
   "Let forms for defining a temporary directory and file.
 TEMP-DIR and TEMP-FILE are the symbols used for the corresponding variables.
 SOURCE-INPLACE specifies whether the TEMP-DIR should be in the same working
-directory as the current buffer."
+directory as the current buffer. Temporary files are named by concatenating
+TEMP-FILE-PREFIX with the current buffer file name."
   `((,temp-dir
      ,@(let ((forms
               (append
@@ -84,7 +86,7 @@ doesn't exist: %s" dir))
      (let ((temporary-file-directory ,temp-dir)
            (basename (file-name-nondirectory (or (buffer-file-name)
                                                  (buffer-name)))))
-       (make-temp-file ".flymake_" nil (concat "_" basename))))))
+       (make-temp-file ,temp-file-prefix nil (concat "_" basename))))))
 
 (defmacro flymake-collection-define--parse-diags
     (title proc-symb diags-symb current-diag-symb source-symb error-parser)
@@ -132,7 +134,7 @@ CURRENT-DIAGS-SYMB, SOURCE-SYMB, ERROR-PARSER are all 
described in
 (cl-defmacro flymake-collection-define
     (name docstring
           &optional &key title command error-parser write-type
-          source-inplace pre-let pre-check)
+          source-inplace pre-let pre-check (temp-file-prefix ".flymake_"))
   "Quickly define a backend function for use with Flymake.
 Define a function NAME which is suitable for use with the variable
 `flymake-diagnostic-functions'. DOCSTRING if given will become the
@@ -192,7 +194,11 @@ used to start the checker process. It should be suitable 
for use as the
 ERROR-PARSER is a lisp-form that should, each time it is evaluated,
 return the next diagnostic from the checker output. The result should be
 a value that can be passed to the `flymake-make-diagnostic' function. Once
-there are no more diagnostics to parse this form should evaluate to nil."
+there are no more diagnostics to parse this form should evaluate to nil.
+
+TEMP-FILE-PREFIX overrides the prefix of temporary file names created by
+the checker. This is useful for checker programs that have issues running
+on hidden files."
   (declare (indent defun) (doc-string 2))
   (unless lexical-binding
     (error "Need lexical-binding for flymake-collection-define (%s)" name))
@@ -228,7 +234,7 @@ there are no more diagnostics to parse this form should 
evaluate to nil."
        (let* ((,source-symb (current-buffer))
               ,@(when (eq write-type 'file)
                   (flymake-collection-define--temp-file
-                   temp-dir-symb temp-file-symb source-inplace))
+                   temp-dir-symb temp-file-symb source-inplace 
temp-file-prefix))
               ,@pre-let)
          ;; With vars defined, do pre-check.
          ,@(when pre-check
@@ -441,13 +447,14 @@ For an example of this macro in action, see 
`flymake-collection-pycodestyle'."
 
 (cl-defmacro flymake-collection-define-rx
     (name docstring
-          &optional &key title command write-type source-inplace pre-let 
pre-check regexps)
+          &optional &key title command write-type
+          source-inplace pre-let pre-check temp-file-prefix regexps)
   "`flymake-collection-define' helper using `rx' syntax to parse diagnostics.
 This helper macro adapts `flymake-collection-define' to use an error-parser
 built from a collections of REGEXPS (see 
`flymake-collection-define--parse-rx').
 
 See `flymake-collection-define' for a description of NAME, DOCSTRING, TITLE,
-COMMAND,WRITE-TYPE, SOURCE-INPLACE, PRE-LET, and PRE-CHECK."
+COMMAND,WRITE-TYPE, SOURCE-INPLACE, PRE-LET, PRE-CHECK, and TEMP-FILE-PREFIX."
   (declare (indent defun) (doc-string 2))
   `(flymake-collection-define ,name
      ,docstring
@@ -457,6 +464,7 @@ COMMAND,WRITE-TYPE, SOURCE-INPLACE, PRE-LET, and PRE-CHECK."
      :source-inplace ,source-inplace
      :pre-let ,pre-let
      :pre-check ,pre-check
+     :temp-file-prefix ,temp-file-prefix
      :error-parser
      (flymake-collection-define--parse-rx ,regexps)))
 
diff --git a/tests/checkers/installers/mypy.bash 
b/tests/checkers/installers/mypy.bash
new file mode 100755
index 0000000000..507e8e903a
--- /dev/null
+++ b/tests/checkers/installers/mypy.bash
@@ -0,0 +1 @@
+python3.8 -m pip install mypy
diff --git a/tests/checkers/test-cases/mypy.yml 
b/tests/checkers/test-cases/mypy.yml
new file mode 100644
index 0000000000..6f3fe27229
--- /dev/null
+++ b/tests/checkers/test-cases/mypy.yml
@@ -0,0 +1,47 @@
+---
+checker: flymake-collection-mypy
+tests:
+  - name: no-lints
+    file: |
+      """A test case with no output from mypy."""
+
+      print("hello world")
+    lints: []
+  - name: error-operator
+    file: |
+      """Test parsing an error."""
+
+      "x" + 1
+    lints:
+      - point: [3, 6]
+        level: error
+        message: operator Unsupported operand types for + ("str" and "int") 
(mypy)
+  - name: error-attr-defined
+    file: |
+      """Test parsing an error with a hyphen in the id."""
+
+      x = 1
+      x.foo
+    lints:
+      - point: [4, 0]
+        level: error
+        message: attr-defined "int" has no attribute "foo" (mypy)
+  - name: note-reveal
+    file: |
+      """Test parsing a note."""
+
+      foo = "bar"
+      reveal_type(foo)
+    lints:
+      - point: [4, 12]
+        level: note
+        message: Revealed type is "builtins.str" (mypy)
+  - name: error-syntax
+    file: |
+      """Test syntax error."""
+
+      class Foo:
+    lints:
+      - point: [3, 0]
+        level: error
+        message: syntax unexpected EOF while parsing (mypy)



reply via email to

[Prev in Thread] Current Thread [Next in Thread]