17
17
import argparse
18
18
import os .path as osp
19
19
import re
20
+ import sys
21
+ import inspect
22
+ import paddle
23
+
24
+
25
+ def add_path (path ):
26
+ if path not in sys .path :
27
+ sys .path .insert (0 , path )
28
+
29
+
30
+ this_dir = osp .dirname (__file__ )
31
+ # Add docs/api to PYTHONPATH
32
+ add_path (osp .abspath (osp .join (this_dir , '..' , 'docs' , 'api' )))
33
+ from extract_api_from_docs import extract_params_desc_from_rst_file
20
34
21
35
arguments = [
22
36
# flags, dest, type, default, help
@@ -43,49 +57,143 @@ def parse_args():
43
57
return args
44
58
45
59
60
+ def _check_params_in_description (rstfilename , paramstr ):
61
+ flag = True
62
+ params_intitle = []
63
+ if paramstr :
64
+ params_intitle = paramstr .split (
65
+ ', '
66
+ ) # is there any parameter with default value of type list/tuple? may break this.
67
+ funcdescnode = extract_params_desc_from_rst_file (rstfilename )
68
+ if funcdescnode :
69
+ items = funcdescnode .children [1 ].children [0 ].children
70
+ if len (items ) != len (params_intitle ):
71
+ flag = False
72
+ print (f'check failed (parammeters description): { rstfilename } ' )
73
+ else :
74
+ for i in range (len (items )):
75
+ pname_intitle = params_intitle [i ].split ('=' )[0 ].strip ()
76
+ mo = re .match (r'(\w+)\b.*' , items [i ].children [0 ].astext ())
77
+ if mo :
78
+ pname_indesc = mo .group (1 )
79
+ if pname_indesc != pname_intitle :
80
+ flag = False
81
+ print (
82
+ f'check failed (parammeters description): { rstfilename } , { pname_indesc } != { pname_intitle } '
83
+ )
84
+ else :
85
+ flag = False
86
+ print (
87
+ f'check failed (parammeters description): { rstfilename } , param name not found in { i } paragraph.'
88
+ )
89
+ else :
90
+ if params_intitle :
91
+ print (
92
+ f'check failed (parameters description not found): { rstfilename } , { params_intitle } .'
93
+ )
94
+ flag = False
95
+ return flag
96
+
97
+
98
+ def _check_params_in_description_with_fullargspec (rstfilename , funcname ):
99
+ flag = True
100
+ funcspec = inspect .getfullargspec (eval (funcname ))
101
+ funcdescnode = extract_params_desc_from_rst_file (rstfilename )
102
+ if funcdescnode :
103
+ items = funcdescnode .children [1 ].children [0 ].children
104
+ params_inspec = funcspec .args
105
+ if len (items ) != len (params_inspec ):
106
+ flag = False
107
+ print (f'check failed (parammeters description): { rstfilename } ' )
108
+ else :
109
+ for i in range (len (items )):
110
+ pname_intitle = params_inspec [i ]
111
+ mo = re .match (r'(\w+)\b.*' , items [i ].children [0 ].astext ())
112
+ if mo :
113
+ pname_indesc = mo .group (1 )
114
+ if pname_indesc != pname_intitle :
115
+ flag = False
116
+ print (
117
+ f'check failed (parammeters description): { rstfilename } , { pname_indesc } != { pname_intitle } '
118
+ )
119
+ else :
120
+ flag = False
121
+ print (
122
+ f'check failed (parammeters description): { rstfilename } , param name not found in { i } paragraph.'
123
+ )
124
+ else :
125
+ if funcspec .args :
126
+ print (
127
+ f'check failed (parameters description not found): { rstfilename } , { funcspec .args } .'
128
+ )
129
+ flag = False
130
+ return flag
131
+
132
+
46
133
def check_api_parameters (rstfiles , apiinfo ):
47
134
"""check function's parameters same as its origin definition.
48
135
49
136
such as `.. py:function:: paddle.version.cuda()`
137
+
138
+ class类别的文档,其成员函数的说明有好多。且class标题还有好多不写参数,暂时都跳过吧
50
139
"""
51
- pat = re .compile (r'^\.\.\s+py:function::\s+(\S+)\s*\(\s*(.*)\s*\)\s*$' )
140
+ pat = re .compile (
141
+ r'^\.\.\s+py:(method|function|class)::\s+(\S+)\s*\(\s*(.*)\s*\)\s*$' )
52
142
check_passed = []
53
143
check_failed = []
54
144
api_notfound = []
55
145
for rstfile in rstfiles :
56
- with open (osp .join ('../docs' , rstfile ), 'r' ) as rst_fobj :
146
+ rstfilename = osp .join ('../docs' , rstfile )
147
+ print (f'checking : { rstfile } ' )
148
+ with open (rstfilename , 'r' ) as rst_fobj :
57
149
func_found = False
58
150
for line in rst_fobj :
59
151
mo = pat .match (line )
60
152
if mo :
61
153
func_found = True
62
- funcname = mo .group (1 )
63
- paramstr = mo .group (2 )
154
+ functype = mo .group (1 )
155
+ if functype not in ('function' , 'method' ):
156
+ check_passed .append (rstfile )
157
+ funcname = mo .group (2 )
158
+ paramstr = mo .group (3 )
64
159
flag = False
65
160
for apiobj in apiinfo .values ():
66
161
if 'all_names' in apiobj and funcname in apiobj [
67
162
'all_names' ]:
68
- if 'args' in apiobj and paramstr == apiobj ['args' ]:
69
- flag = True
163
+ if 'args' in apiobj :
164
+ if paramstr == apiobj ['args' ]:
165
+ print (
166
+ f'check func:{ funcname } in { rstfilename } with { paramstr } '
167
+ )
168
+ flag = _check_params_in_description (
169
+ rstfilename , paramstr )
170
+ else :
171
+ print (
172
+ f'check func:{ funcname } in { rstfilename } with { paramstr } , but different with json\' s { apiobj ["args" ]} '
173
+ )
174
+ flag = _check_params_in_description (
175
+ rstfilename , paramstr )
176
+ else : # paddle.abs class_method does not have `args` in its json item.
177
+ print (
178
+ f'check func:{ funcname } in { rstfilename } with its FullArgSpec'
179
+ )
180
+ flag = _check_params_in_description_with_fullargspec (
181
+ rstfilename , funcname )
70
182
break
71
183
if flag :
72
184
check_passed .append (rstfile )
185
+ print (f'check success: { rstfile } ' )
73
186
else :
74
187
check_failed .append (rstfile )
188
+ print (f'check failed: { rstfile } ' )
75
189
break
76
190
if not func_found :
77
191
api_notfound .append (rstfile )
192
+ print (f'check failed (object not found): { rstfile } ' )
193
+ print (f'checking done: { rstfile } ' )
78
194
return check_passed , check_failed , api_notfound
79
195
80
196
81
- def check_api_params_desc ():
82
- """chech the Args Segment.
83
-
84
- 是不是用docutils来解析rst文件的好?不要暴力正则表达式了?
85
- """
86
- ...
87
-
88
-
89
197
if __name__ == '__main__' :
90
198
args = parse_args ()
91
199
rstfiles = [fn for fn in args .rst_files .split (' ' ) if fn ]
0 commit comments