codekingpro/portable-devtools
115k
1"""
2
3Rules for building C/API module with f2py2e.
4
5Copyright 1999 -- 2011 Pearu Peterson all rights reserved.
6Copyright 2011 -- present NumPy Developers.
7Permission to use, modify, and distribute this software is given under the
8terms of the NumPy License.
9
10NO WARRANTY IS EXPRESSED OR IMPLIED. USE AT YOUR OWN RISK.
11"""
12import copy
13
14from ._isocbind import isoc_kindmap
15from .auxfuncs import (
16 getfortranname,
17 isexternal,
18 isfunction,
19 isfunction_wrap,
20 isintent_in,
21 isintent_out,
22 islogicalfunction,
23 ismoduleroutine,
24 isscalar,
25 issubroutine,
26 issubroutine_wrap,
27 outmess,
28 show,
29)
30
31
32def var2fixfortran(vars, a, fa=None, f90mode=None):
33 if fa is None:
34 fa = a
35 if a not in vars:
36 show(vars)
37 outmess(f'var2fixfortran: No definition for argument "{a}".\n')
38 return ''
39 if 'typespec' not in vars[a]:
40 show(vars[a])
41 outmess(f'var2fixfortran: No typespec for argument "{a}".\n')
42 return ''
43 vardef = vars[a]['typespec']
44 if vardef == 'type' and 'typename' in vars[a]:
45 vardef = f"{vardef}({vars[a]['typename']})"
46 selector = {}
47 lk = ''
48 if 'kindselector' in vars[a]:
49 selector = vars[a]['kindselector']
50 lk = 'kind'
51 elif 'charselector' in vars[a]:
52 selector = vars[a]['charselector']
53 lk = 'len'
54 if '*' in selector:
55 if f90mode:
56 if selector['*'] in ['*', ':', '(*)']:
57 vardef = f'{vardef}(len=*)'
58 else:
59 vardef = f"{vardef}({lk}={selector['*']})"
60 elif selector['*'] in ['*', ':']:
61 vardef = f"{vardef}*({selector['*']})"
62 else:
63 vardef = f"{vardef}*{selector['*']}"
64 elif 'len' in selector:
65 vardef = f"{vardef}(len={selector['len']}"
66 if 'kind' in selector:
67 vardef = f"{vardef},kind={selector['kind']})"
68 else:
69 vardef = f'{vardef})'
70 elif 'kind' in selector:
71 vardef = f"{vardef}(kind={selector['kind']})"
72
73 vardef = f'{vardef} {fa}'
74 if 'dimension' in vars[a]:
75 vardef = f"{vardef}({','.join(vars[a]['dimension'])})"
76 return vardef
77
78def useiso_c_binding(rout):
79 useisoc = False
80 for value in rout['vars'].values():
81 kind_value = value.get('kindselector', {}).get('kind')
82 if kind_value in isoc_kindmap:
83 return True
84 return useisoc
85
86def createfuncwrapper(rout, signature=0):
87 assert isfunction(rout)
88
89 extra_args = []
90 vars = rout['vars']
91 for a in rout['args']:
92 v = rout['vars'][a]
93 for i, d in enumerate(v.get('dimension', [])):
94 if d == ':':
95 dn = f'f2py_{a}_d{i}'
96 dv = {'typespec': 'integer', 'intent': ['hide']}
97 dv['='] = f'shape({a}, {i})'
98 extra_args.append(dn)
99 vars[dn] = dv
100 v['dimension'][i] = dn
101 rout['args'].extend(extra_args)
102 need_interface = bool(extra_args)
103
104 ret = ['']
105
106 def add(line, ret=ret):
107 ret[0] = f'{ret[0]}\n {line}'
108 name = rout['name']
109 fortranname = getfortranname(rout)
110 f90mode = ismoduleroutine(rout)
111 newname = f'{name}f2pywrap'
112
113 if newname not in vars:
114 vars[newname] = vars[name]
115 args = [newname] + rout['args'][1:]
116 else:
117 args = [newname] + rout['args']
118
119 l_tmpl = var2fixfortran(vars, name, '@@@NAME@@@', f90mode)
120 if l_tmpl[:13] == 'character*(*)':
121 if f90mode:
122 l_tmpl = 'character(len=10)' + l_tmpl[13:]
123 else:
124 l_tmpl = 'character*10' + l_tmpl[13:]
125 charselect = vars[name]['charselector']
126 if charselect.get('*', '') == '(*)':
127 charselect['*'] = '10'
128
129 l1 = l_tmpl.replace('@@@NAME@@@', newname)
130 rl = None
131
132 useisoc = useiso_c_binding(rout)
133 sargs = ', '.join(args)
134 if f90mode:
135 # gh-23598 fix warning
136 # Essentially, this gets called again with modules where the name of the
137 # function is added to the arguments, which is not required, and removed
138 sargs = sargs.replace(f"{name}, ", '')
139 args = [arg for arg in args if arg != name]
140 rout['args'] = args
141 add(f"subroutine f2pywrap_{rout['modulename']}_{name} ({sargs})")
142 if not signature:
143 add(f"use {rout['modulename']}, only : {fortranname}")
144 if useisoc:
145 add('use iso_c_binding')
146 else:
147 add(f'subroutine f2pywrap{name} ({sargs})')
148 if useisoc:
149 add('use iso_c_binding')
150 if not need_interface:
151 add(f'external {fortranname}')
152 rl = l_tmpl.replace('@@@NAME@@@', '') + ' ' + fortranname
153
154 if need_interface:
155 for line in rout['saved_interface'].split('\n'):
156 if line.lstrip().startswith('use ') and '__user__' not in line:
157 add(line)
158
159 args = args[1:]
160 dumped_args = []
161 for a in args:
162 if isexternal(vars[a]):
163 add(f'external {a}')
164 dumped_args.append(a)
165 for a in args:
166 if a in dumped_args:
167 continue
168 if isscalar(vars[a]):
169 add(var2fixfortran(vars, a, f90mode=f90mode))
170 dumped_args.append(a)
171 for a in args:
172 if a in dumped_args:
173 continue
174 if isintent_in(vars[a]):
175 add(var2fixfortran(vars, a, f90mode=f90mode))
176 dumped_args.append(a)
177 for a in args:
178 if a in dumped_args:
179 continue
180 add(var2fixfortran(vars, a, f90mode=f90mode))
181
182 add(l1)
183 if rl is not None:
184 add(rl)
185
186 if need_interface:
187 if f90mode:
188 # f90 module already defines needed interface
189 pass
190 else:
191 add('interface')
192 add(rout['saved_interface'].lstrip())
193 add('end interface')
194
195 sargs = ', '.join([a for a in args if a not in extra_args])
196
197 if not signature:
198 if islogicalfunction(rout):
199 add(f'{newname} = .not.(.not.{fortranname}({sargs}))')
200 else:
201 add(f'{newname} = {fortranname}({sargs})')
202 if f90mode:
203 add(f"end subroutine f2pywrap_{rout['modulename']}_{name}")
204 else:
205 add('end')
206 return ret[0]
207
208
209def createsubrwrapper(rout, signature=0):
210 assert issubroutine(rout)
211
212 extra_args = []
213 vars = rout['vars']
214 for a in rout['args']:
215 v = rout['vars'][a]
216 for i, d in enumerate(v.get('dimension', [])):
217 if d == ':':
218 dn = f'f2py_{a}_d{i}'
219 dv = {'typespec': 'integer', 'intent': ['hide']}
220 dv['='] = f'shape({a}, {i})'
221 extra_args.append(dn)
222 vars[dn] = dv
223 v['dimension'][i] = dn
224 rout['args'].extend(extra_args)
225 need_interface = bool(extra_args)
226
227 ret = ['']
228
229 def add(line, ret=ret):
230 ret[0] = f'{ret[0]}\n {line}'
231 name = rout['name']
232 fortranname = getfortranname(rout)
233 f90mode = ismoduleroutine(rout)
234
235 args = rout['args']
236
237 useisoc = useiso_c_binding(rout)
238 sargs = ', '.join(args)
239 if f90mode:
240 add(f"subroutine f2pywrap_{rout['modulename']}_{name} ({sargs})")
241 if useisoc:
242 add('use iso_c_binding')
243 if not signature:
244 add(f"use {rout['modulename']}, only : {fortranname}")
245 else:
246 add(f'subroutine f2pywrap{name} ({sargs})')
247 if useisoc:
248 add('use iso_c_binding')
249 if not need_interface:
250 add(f'external {fortranname}')
251
252 if need_interface:
253 for line in rout['saved_interface'].split('\n'):
254 if line.lstrip().startswith('use ') and '__user__' not in line:
255 add(line)
256
257 dumped_args = []
258 for a in args:
259 if isexternal(vars[a]):
260 add(f'external {a}')
261 dumped_args.append(a)
262 for a in args:
263 if a in dumped_args:
264 continue
265 if isscalar(vars[a]):
266 add(var2fixfortran(vars, a, f90mode=f90mode))
267 dumped_args.append(a)
268 for a in args:
269 if a in dumped_args:
270 continue
271 add(var2fixfortran(vars, a, f90mode=f90mode))
272
273 if need_interface:
274 if f90mode:
275 # f90 module already defines needed interface
276 pass
277 else:
278 add('interface')
279 for line in rout['saved_interface'].split('\n'):
280 if line.lstrip().startswith('use ') and '__user__' in line:
281 continue
282 add(line)
283 add('end interface')
284
285 sargs = ', '.join([a for a in args if a not in extra_args])
286
287 if not signature:
288 add(f'call {fortranname}({sargs})')
289 if f90mode:
290 add(f"end subroutine f2pywrap_{rout['modulename']}_{name}")
291 else:
292 add('end')
293 return ret[0]
294
295
296def assubr(rout):
297 if isfunction_wrap(rout):
298 fortranname = getfortranname(rout)
299 name = rout['name']
300 outmess('\t\tCreating wrapper for Fortran function "%s"("%s")...\n' % (
301 name, fortranname))
302 rout = copy.copy(rout)
303 fname = name
304 rname = fname
305 if 'result' in rout:
306 rname = rout['result']
307 rout['vars'][fname] = rout['vars'][rname]
308 fvar = rout['vars'][fname]
309 if not isintent_out(fvar):
310 if 'intent' not in fvar:
311 fvar['intent'] = []
312 fvar['intent'].append('out')
313 flag = 1
314 for i in fvar['intent']:
315 if i.startswith('out='):
316 flag = 0
317 break
318 if flag:
319 fvar['intent'].append(f'out={rname}')
320 rout['args'][:] = [fname] + rout['args']
321 return rout, createfuncwrapper(rout)
322 if issubroutine_wrap(rout):
323 fortranname = getfortranname(rout)
324 name = rout['name']
325 outmess('\t\tCreating wrapper for Fortran subroutine "%s"("%s")...\n'
326 % (name, fortranname))
327 rout = copy.copy(rout)
328 return rout, createsubrwrapper(rout)
329 return rout, ''
330 