From 8367da8a8f39814780803d05ad75aa334d548d60 Mon Sep 17 00:00:00 2001 From: Daniel Date: Mon, 4 Nov 2024 16:58:33 +0100 Subject: [PATCH 1/3] FIX: Python arguments. This should fix #505 "Any snippet using python-args-to-docstring returns Wrong type argument: listp" and also adds type hints to the docstring again. --- snippets/python-mode/.yas-setup.el | 69 +++++++++++++++++++++++++++--- 1 file changed, 62 insertions(+), 7 deletions(-) diff --git a/snippets/python-mode/.yas-setup.el b/snippets/python-mode/.yas-setup.el index e9145efd..f7a40c59 100644 --- a/snippets/python-mode/.yas-setup.el +++ b/snippets/python-mode/.yas-setup.el @@ -1,21 +1,48 @@ ;;; -*- lexical-binding: t -*- + +; Copyright (C) miscellaneous contributors, see git history +; Copyright (C) 2024 Daniel Hornung +; +; This program is free software: you can redistribute it and/or modify +; it under the terms of the GNU General Public License as +; published by the Free Software Foundation, either version 3 of the +; License, or (at your option) any later version. +; +; This program is distributed in the hope that it will be useful, +; but WITHOUT ANY WARRANTY; without even the implied warranty of +; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +; GNU General Public License for more details. +; +; You should have received a copy of the GNU General Public License +; along with this program. If not, see . + (require 'yasnippet) (defvar yas-text) (defvar python-split-arg-arg-regex -"\\([[:alnum:]*]+\\)\\(:[[:blank:]]*[[:alpha:]]*\\)?\\([[:blank:]]*=[[:blank:]]*[[:alnum:]]*\\)?" + "\\([[:alnum:]*]+\\)\\(:[[:blank:]]*\\([][:alpha:][]*\\)\\)?\\([[:blank:]]*=[[:blank:]]*\\([[:alnum:]]*\\)\\)?" "Regular expression matching an argument of a python function. -First group should give the argument name.") +Groups: +- 1: the argument name +- 3: the type +- 5: the default value") (defvar python-split-arg-separator "[[:space:]]*,[[:space:]]*" "Regular expression matching the separator in a list of argument.") (defun python-split-args (arg-string) - "Split a python argument string ARG-STRING into a tuple of argument names." - (mapcar (lambda (x) + "Split python argument string ARG-STRING. + +The result is a list ((name, type, default), ...) of argument names, types and +default values." + (mapcar (lambda (x) ; organize output (when (string-match python-split-arg-arg-regex x) - (match-string-no-properties 1 x))) + (list + (match-string-no-properties 1 x) ; name + (match-string-no-properties 3 x) ; type + (match-string-no-properties 5 x) ; default + ))) (split-string arg-string python-split-arg-separator t))) (defun python-args-to-docstring () @@ -26,7 +53,9 @@ First group should give the argument name.") (formatted-args (mapconcat (lambda (x) (concat (nth 0 x) (make-string (- max-len (length (nth 0 x))) ? ) " -- " - (if (nth 1 x) (concat "\(default " (nth 1 x) "\)")))) + (if (nth 1 x) (concat (nth 1 x) ": ")) + (if (nth 2 x) (concat "\(default " (nth 2 x) "\)")) + )) args indent))) (unless (string= formatted-args "") @@ -36,7 +65,11 @@ First group should give the argument name.") "return docstring format for the python arguments in yas-text" (let* ((args (python-split-args yas-text)) (format-arg (lambda(arg) - (concat (nth 0 arg) " : " (if (nth 1 arg) ", optional") "\n"))) + (concat (nth 0 arg) " : " ; name + (if (nth 1 arg) (nth 1 arg)) ; type TODO handle Optional[Foo] correctly + (if (nth 2 arg) (concat (when (nth 1 arg) ", ") + "default=" (nth 2 arg))) ; default + "\n"))) (formatted-params (mapconcat format-arg args "\n")) (formatted-ret (mapconcat format-arg (list (list "out")) "\n"))) (unless (string= formatted-params "") @@ -44,3 +77,25 @@ First group should give the argument name.") (list "\nParameters\n----------" formatted-params "\nReturns\n-------" formatted-ret) "\n")))) + + +;; Tests + +(ert-deftest test-split () + "For starters, only test a single string for expected output." + (should (equal + (python-split-args "foo=3, bar: int = 2, baz: Optional[MyType], foobar") + (list '("foo" nil "3") + '("bar" "int" "2") + '("baz" "Optional[MyType]" nil) + '("foobar" nil nil))) + )) + +;; For manual testing and development: + +;; (setq yas-text "foo=3, bar: int = 2, baz: Optional[MyType], foobar") +;; (split-string yas-text python-split-arg-separator t) +;; +;; (python-split-args yas-text) +;; (python-args-to-docstring) +;; (python-args-to-docstring-numpy) From c4c52c3ce2538d8b901b31f189f0cf17e8253f41 Mon Sep 17 00:00:00 2001 From: Daniel Date: Tue, 5 Nov 2024 09:14:54 +0100 Subject: [PATCH 2/3] FIX: More relaxed regexes. --- snippets/python-mode/.yas-setup.el | 35 +++++++++++++++++++++++++----- 1 file changed, 29 insertions(+), 6 deletions(-) diff --git a/snippets/python-mode/.yas-setup.el b/snippets/python-mode/.yas-setup.el index f7a40c59..71ad9494 100644 --- a/snippets/python-mode/.yas-setup.el +++ b/snippets/python-mode/.yas-setup.el @@ -19,8 +19,25 @@ (require 'yasnippet) (defvar yas-text) -(defvar python-split-arg-arg-regex - "\\([[:alnum:]*]+\\)\\(:[[:blank:]]*\\([][:alpha:][]*\\)\\)?\\([[:blank:]]*=[[:blank:]]*\\([[:alnum:]]*\\)\\)?" +(defvar yas-python-regex-identifier "[[:alnum:]_]+" "Simplified Python identifier.") +(defvar yas-python-regex-quoted-or-identifier (concat + "\\(" + yas-python-regex-identifier + "\\)" + "\\|" "\".*\"" + "\\|" "'.*'" + ) + "Simplified Python identifier or quoted string. +Does not work well with multiple or escaped quotes") + +(defvar python-split-arg-regex + (concat + "\\(" yas-python-regex-identifier "\\)" ; name + "\\(:[[:blank:]]*\\([][:alpha:]_[]*\\)\\)?" ; type + "\\([[:blank:]]*=[[:blank:]]*\\(" + yas-python-regex-quoted-or-identifier ; default + "\\)\\)?" + ) "Regular expression matching an argument of a python function. Groups: - 1: the argument name @@ -37,7 +54,7 @@ Groups: The result is a list ((name, type, default), ...) of argument names, types and default values." (mapcar (lambda (x) ; organize output - (when (string-match python-split-arg-arg-regex x) + (when (string-match python-split-arg-regex x) (list (match-string-no-properties 1 x) ; name (match-string-no-properties 3 x) ; type @@ -84,10 +101,10 @@ default values." (ert-deftest test-split () "For starters, only test a single string for expected output." (should (equal - (python-split-args "foo=3, bar: int = 2, baz: Optional[MyType], foobar") - (list '("foo" nil "3") + (python-split-args "_foo='this', bar: int = 2, baz: Optional[My_Type], foobar") + (list '("_foo" nil "'this'") '("bar" "int" "2") - '("baz" "Optional[MyType]" nil) + '("baz" "Optional[My_Type]" nil) '("foobar" nil nil))) )) @@ -96,6 +113,12 @@ default values." ;; (setq yas-text "foo=3, bar: int = 2, baz: Optional[MyType], foobar") ;; (split-string yas-text python-split-arg-separator t) ;; +;; (save-match-data +;; (setq my-string "_foo: my_bar = 'this'") +;; (string-match python-split-arg-regex my-string) +;; (match-string 5 my-string) +;; ) +;; ;; (python-split-args yas-text) ;; (python-args-to-docstring) ;; (python-args-to-docstring-numpy) From ba662f9cf73a04695c1d8658b8db71c9152a181b Mon Sep 17 00:00:00 2001 From: Daniel Date: Tue, 5 Nov 2024 10:38:08 +0100 Subject: [PATCH 3/3] ENH: Removing `self` from python arguments in docstring. --- snippets/python-mode/.yas-setup.el | 31 +++++++++++++++++++++--------- 1 file changed, 22 insertions(+), 9 deletions(-) diff --git a/snippets/python-mode/.yas-setup.el b/snippets/python-mode/.yas-setup.el index 71ad9494..52ea2da7 100644 --- a/snippets/python-mode/.yas-setup.el +++ b/snippets/python-mode/.yas-setup.el @@ -52,15 +52,20 @@ Groups: "Split python argument string ARG-STRING. The result is a list ((name, type, default), ...) of argument names, types and -default values." - (mapcar (lambda (x) ; organize output - (when (string-match python-split-arg-regex x) - (list - (match-string-no-properties 1 x) ; name - (match-string-no-properties 3 x) ; type - (match-string-no-properties 5 x) ; default - ))) - (split-string arg-string python-split-arg-separator t))) +default values. An argument named `self` is omitted." + (remove + nil + (mapcar (lambda (x) ; organize output + (when (and + (not (equal "self" x)) + (string-match python-split-arg-regex x) + ) + (list + (match-string-no-properties 1 x) ; name + (match-string-no-properties 3 x) ; type + (match-string-no-properties 5 x) ; default + ))) + (split-string arg-string python-split-arg-separator t)))) (defun python-args-to-docstring () "Return docstring format for the python arguments in yas-text." @@ -108,6 +113,14 @@ default values." '("foobar" nil nil))) )) +(ert-deftest test-argument-self () + "If an argument is called `self`, it must be omitted" + (should (equal + (python-split-args "self, _foo=\"this\"") + (list '("_foo" nil "\"this\"") + )) + )) + ;; For manual testing and development: ;; (setq yas-text "foo=3, bar: int = 2, baz: Optional[MyType], foobar")