-
Notifications
You must be signed in to change notification settings - Fork 33
/
Copy pathmake_wrapped_ufuncs.py
225 lines (190 loc) · 6.38 KB
/
make_wrapped_ufuncs.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
"""
Script that generates _wrapped_ufuncs.py based on the output
of make_ufuncs.py.
"""
from pathlib import Path
from _utilities import Bunch
from matlab_parser import get_complete_sigdict, get_helpdict
from c_header_parser import get_signatures, parse_signatures
from docstring_parts import parameters, return_overrides
from docstring_utils import (paragraphs,
fix_outputs_doc,
docstring_from_sections)
basedir = Path(__file__).parent.parent
# Functions that are Matlab subroutines, or exclusive to
# the C and not needed; we don't need to expose them.
blacklist = {'ct_freezing_exact',
'pt0_cold_ice_poly',
'pt_from_pot_enthalpy_ice_poly_dh',
't_freezing_exact',
'sa_p_inrange',
}
wrapper_head = '''
"""
Auto-generated wrapper for C ufunc extension; do not edit!
"""
from . import _gsw_ufuncs
from ._utilities import match_args_return
'''
## Alternatives: The first was the original, but it did not provide a way to
# tell the decorator about the signature of the ufunc. The second solved that
# problem, but failed to provide the argument names for the signature in the
# help function and the ipython "?" functionality.
# wrapper_template = '''
# @match_args_return
# def %(funcname)s(%(args)s):
# """%(doc)s
# """
# return _gsw_ufuncs.%(ufuncname)s(%(args)s)
# '''
# wrapper_template = """
# %(funcname)s = match_args_return(_gsw_ufuncs.%(ufuncname)s)
# %(funcname)s.__doc__ = '''%(doc)s
# '''
# """
# Make a Python function with the proper list of arguments; add the 'types'
# attribute for the use of the decorator; then use the decorator in its
# function form.
wrapper_template = '''
def %(funcname)s(%(args)s):
"""%(doc)s
"""
return _gsw_ufuncs.%(ufuncname)s(%(args)s)
%(funcname)s.types = _gsw_ufuncs.%(ufuncname)s.types
%(funcname)s = match_args_return(%(funcname)s)
'''
def get_argnames(ufname):
try:
msig = Bunch(msigdict[ufname])
csig = Bunch(csigdict[ufname])
except KeyError:
return None
cnames = csig.argnames[:]
mnames = msig.argnames[:]
nc, nm = len(cnames), len(mnames)
if nc < nm:
print('%s: truncating argument list, %s, %s' % (
ufname, cnames, mnames))
mnames = mnames[:nc]
argnames = []
for ac, am in zip(cnames, mnames):
if am == 'long':
am = 'lon'
if ac == am.lower():
argnames.append(am)
else:
raise RuntimeError("arg mismatch: %s, %s" % (
csig.argnames, msig.argnames))
return argnames
def get_argname_set():
# This is not currently used internally.
argset = set()
for ufname in ufunclist:
args = get_argnames(ufname)
if args is not None:
argset.update(args)
return argset
def get_ufnames_by_arg():
# This is not currently used internally.
argdict = dict()
for ufname in ufunclist:
args = get_argnames(ufname)
if args is None:
continue
for arg in args:
if arg in argdict:
argdict[arg].append(ufname)
else:
argdict[arg] = [ufname]
return argdict
def get_outnames(ufname):
# This is currently used only in get_outname_set, which is not used internally.
try:
msig = Bunch(msigdict[ufname])
except KeyError:
return None
mnames = msig.outnames[:]
outnames = []
for am in mnames:
if am == 'long':
am = 'lon'
outnames.append(am)
return outnames
def get_outname_set():
# This is not currently used internally.
argset = set()
for ufname in ufunclist:
args = get_outnames(ufname)
if args is not None:
argset.update(args)
return argset
def uf_wrapper(ufname):
argnames = get_argnames(ufname)
if argnames is None:
print(f"in uf_wrapper, ufname is {ufname}, argnames is None")
return None
argstr = ', '.join(argnames)
msig = Bunch(msigdict[ufname])
subs = dict(ufuncname=ufname,
funcname=msig['name'],
args=argstr,
)
helpdict = get_helpdict(msig['path'])
sections = {}
if 'DESCRIPTION' not in helpdict:
helpdict['DESCRIPTION'] = helpdict["summary"]
sections["Notes"] = helpdict["all"]
description_paragraphs = paragraphs(helpdict['DESCRIPTION'])
sections["Head"] = description_paragraphs[0]
if len(description_paragraphs) > 1:
lines = []
for p in description_paragraphs[1:]:
lines.extend(p)
lines.append("\n")
sections["Notes"] = lines
plist = []
for arg in argnames:
plist.append('%s : array-like' % arg)
for line in parameters[arg].split('\n'):
plist.append(" %s" % line)
sections['Parameters'] = plist
# I think we can assume OUTPUT will be present, but just
# in case, we check for it. Maybe remove this later.
if 'OUTPUT' in helpdict:
outdoc = fix_outputs_doc(helpdict['OUTPUT'])
else:
outdoc = ['double, array']
if ufname in return_overrides:
outdoc = return_overrides[ufname]
sections['Returns'] = outdoc
if "REFERENCES" in helpdict:
sections["References"] = [line.strip() for line in helpdict["REFERENCES"]]
doc = docstring_from_sections(sections)
subs['doc'] = doc
return wrapper_template % subs
if __name__ == '__main__':
srcdir = basedir.joinpath('src')
with open(srcdir.joinpath('_ufuncs.list')) as f:
ufunclist = [name.strip() for name in f.readlines()]
ufunclist = [name for name in ufunclist if name not in blacklist]
wrapmod = basedir.joinpath('gsw', '_wrapped_ufuncs.py')
msigdict = get_complete_sigdict()
csigdict = parse_signatures(get_signatures(srcdir=srcdir))
wrapped_ufnames = []
with wrapmod.open('w') as f:
f.write(wrapper_head)
for ufname in ufunclist:
try:
wrapped = uf_wrapper(ufname)
if wrapped is None:
continue
except RuntimeError as err:
print(ufname, err)
if wrapped is None:
print("failed:", ufname)
else:
f.write(wrapped)
wrapped_ufnames.append(ufname)
wrapped_ufnames.sort()
with open(srcdir.joinpath('_wrapped_ufuncs.list'), 'w') as f:
f.write('\n'.join(wrapped_ufnames) + '\n')