Skip to content

Commit aa5f9b9

Browse files
quazgarDaniel
and
Daniel
authored
FIX: Python arguments. (#509)
* 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. * FIX: More relaxed regexes. * ENH: Removing `self` from python arguments in docstring. --------- Co-authored-by: Daniel <[email protected]>
1 parent 46945cc commit aa5f9b9

File tree

1 file changed

+101
-10
lines changed

1 file changed

+101
-10
lines changed

snippets/python-mode/.yas-setup.el

+101-10
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,71 @@
11
;;; -*- lexical-binding: t -*-
2+
3+
; Copyright (C) miscellaneous contributors, see git history
4+
; Copyright (C) 2024 Daniel Hornung <[email protected]>
5+
;
6+
; This program is free software: you can redistribute it and/or modify
7+
; it under the terms of the GNU General Public License as
8+
; published by the Free Software Foundation, either version 3 of the
9+
; License, or (at your option) any later version.
10+
;
11+
; This program is distributed in the hope that it will be useful,
12+
; but WITHOUT ANY WARRANTY; without even the implied warranty of
13+
; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14+
; GNU General Public License for more details.
15+
;
16+
; You should have received a copy of the GNU General Public License
17+
; along with this program. If not, see <https://www.gnu.org/licenses/>.
18+
219
(require 'yasnippet)
320
(defvar yas-text)
421

5-
(defvar python-split-arg-arg-regex
6-
"\\([[:alnum:]*]+\\)\\(:[[:blank:]]*[[:alpha:]]*\\)?\\([[:blank:]]*=[[:blank:]]*[[:alnum:]]*\\)?"
22+
(defvar yas-python-regex-identifier "[[:alnum:]_]+" "Simplified Python identifier.")
23+
(defvar yas-python-regex-quoted-or-identifier (concat
24+
"\\("
25+
yas-python-regex-identifier
26+
"\\)"
27+
"\\|" "\".*\""
28+
"\\|" "'.*'"
29+
)
30+
"Simplified Python identifier or quoted string.
31+
Does not work well with multiple or escaped quotes")
32+
33+
(defvar python-split-arg-regex
34+
(concat
35+
"\\(" yas-python-regex-identifier "\\)" ; name
36+
"\\(:[[:blank:]]*\\([][:alpha:]_[]*\\)\\)?" ; type
37+
"\\([[:blank:]]*=[[:blank:]]*\\("
38+
yas-python-regex-quoted-or-identifier ; default
39+
"\\)\\)?"
40+
)
741
"Regular expression matching an argument of a python function.
8-
First group should give the argument name.")
42+
Groups:
43+
- 1: the argument name
44+
- 3: the type
45+
- 5: the default value")
946

1047
(defvar python-split-arg-separator
1148
"[[:space:]]*,[[:space:]]*"
1249
"Regular expression matching the separator in a list of argument.")
1350

1451
(defun python-split-args (arg-string)
15-
"Split a python argument string ARG-STRING into a tuple of argument names."
16-
(mapcar (lambda (x)
17-
(when (string-match python-split-arg-arg-regex x)
18-
(match-string-no-properties 1 x)))
19-
(split-string arg-string python-split-arg-separator t)))
52+
"Split python argument string ARG-STRING.
53+
54+
The result is a list ((name, type, default), ...) of argument names, types and
55+
default values. An argument named `self` is omitted."
56+
(remove
57+
nil
58+
(mapcar (lambda (x) ; organize output
59+
(when (and
60+
(not (equal "self" x))
61+
(string-match python-split-arg-regex x)
62+
)
63+
(list
64+
(match-string-no-properties 1 x) ; name
65+
(match-string-no-properties 3 x) ; type
66+
(match-string-no-properties 5 x) ; default
67+
)))
68+
(split-string arg-string python-split-arg-separator t))))
2069

2170
(defun python-args-to-docstring ()
2271
"Return docstring format for the python arguments in yas-text."
@@ -26,7 +75,9 @@ First group should give the argument name.")
2675
(formatted-args (mapconcat
2776
(lambda (x)
2877
(concat (nth 0 x) (make-string (- max-len (length (nth 0 x))) ? ) " -- "
29-
(if (nth 1 x) (concat "\(default " (nth 1 x) "\)"))))
78+
(if (nth 1 x) (concat (nth 1 x) ": "))
79+
(if (nth 2 x) (concat "\(default " (nth 2 x) "\)"))
80+
))
3081
args
3182
indent)))
3283
(unless (string= formatted-args "")
@@ -36,11 +87,51 @@ First group should give the argument name.")
3687
"return docstring format for the python arguments in yas-text"
3788
(let* ((args (python-split-args yas-text))
3889
(format-arg (lambda(arg)
39-
(concat (nth 0 arg) " : " (if (nth 1 arg) ", optional") "\n")))
90+
(concat (nth 0 arg) " : " ; name
91+
(if (nth 1 arg) (nth 1 arg)) ; type TODO handle Optional[Foo] correctly
92+
(if (nth 2 arg) (concat (when (nth 1 arg) ", ")
93+
"default=" (nth 2 arg))) ; default
94+
"\n")))
4095
(formatted-params (mapconcat format-arg args "\n"))
4196
(formatted-ret (mapconcat format-arg (list (list "out")) "\n")))
4297
(unless (string= formatted-params "")
4398
(mapconcat 'identity
4499
(list "\nParameters\n----------" formatted-params
45100
"\nReturns\n-------" formatted-ret)
46101
"\n"))))
102+
103+
104+
;; Tests
105+
106+
(ert-deftest test-split ()
107+
"For starters, only test a single string for expected output."
108+
(should (equal
109+
(python-split-args "_foo='this', bar: int = 2, baz: Optional[My_Type], foobar")
110+
(list '("_foo" nil "'this'")
111+
'("bar" "int" "2")
112+
'("baz" "Optional[My_Type]" nil)
113+
'("foobar" nil nil)))
114+
))
115+
116+
(ert-deftest test-argument-self ()
117+
"If an argument is called `self`, it must be omitted"
118+
(should (equal
119+
(python-split-args "self, _foo=\"this\"")
120+
(list '("_foo" nil "\"this\"")
121+
))
122+
))
123+
124+
;; For manual testing and development:
125+
126+
;; (setq yas-text "foo=3, bar: int = 2, baz: Optional[MyType], foobar")
127+
;; (split-string yas-text python-split-arg-separator t)
128+
;;
129+
;; (save-match-data
130+
;; (setq my-string "_foo: my_bar = 'this'")
131+
;; (string-match python-split-arg-regex my-string)
132+
;; (match-string 5 my-string)
133+
;; )
134+
;;
135+
;; (python-split-args yas-text)
136+
;; (python-args-to-docstring)
137+
;; (python-args-to-docstring-numpy)

0 commit comments

Comments
 (0)