codekingpro/portable-devtools
115k
1"""
2Build F90 module support for f2py2e.
3
4Copyright 1999 -- 2011 Pearu Peterson all rights reserved.
5Copyright 2011 -- present NumPy Developers.
6Permission to use, modify, and distribute this software is given under the
7terms of the NumPy License.
8
9NO WARRANTY IS EXPRESSED OR IMPLIED. USE AT YOUR OWN RISK.
10"""
11__version__ = "$Revision: 1.27 $"[10:-1]
12
13f2py_version = 'See `f2py -v`'
14
15import numpy as np
16
17from . import capi_maps, func2subr
18
19# The environment provided by auxfuncs.py is needed for some calls to eval.
20# As the needed functions cannot be determined by static inspection of the
21# code, it is safest to use import * pending a major refactoring of f2py.
22from .auxfuncs import *
23from .crackfortran import undo_rmbadname, undo_rmbadname1
24
25options = {}
26
27
28def findf90modules(m):
29 if ismodule(m):
30 return [m]
31 if not hasbody(m):
32 return []
33 ret = []
34 for b in m['body']:
35 if ismodule(b):
36 ret.append(b)
37 else:
38 ret = ret + findf90modules(b)
39 return ret
40
41
42fgetdims1 = """\
43 external f2pysetdata
44 logical ns
45 integer r,i
46 integer(%d) s(*)
47 ns = .FALSE.
48 if (allocated(d)) then
49 do i=1,r
50 if ((size(d,i).ne.s(i)).and.(s(i).ge.0)) then
51 ns = .TRUE.
52 end if
53 end do
54 if (ns) then
55 deallocate(d)
56 end if
57 end if
58 if ((.not.allocated(d)).and.(s(1).ge.1)) then""" % np.intp().itemsize
59
60fgetdims2 = """\
61 end if
62 if (allocated(d)) then
63 do i=1,r
64 s(i) = size(d,i)
65 end do
66 end if
67 flag = 1
68 call f2pysetdata(d,allocated(d))"""
69
70fgetdims2_sa = """\
71 end if
72 if (allocated(d)) then
73 do i=1,r
74 s(i) = size(d,i)
75 end do
76 !s(r) must be equal to len(d(1))
77 end if
78 flag = 2
79 call f2pysetdata(d,allocated(d))"""
80
81
82def buildhooks(pymod):
83 from . import rules
84 ret = {'f90modhooks': [], 'initf90modhooks': [], 'body': [],
85 'need': ['F_FUNC', 'arrayobject.h'],
86 'separatorsfor': {'includes0': '\n', 'includes': '\n'},
87 'docs': ['"Fortran 90/95 modules:\\n"'],
88 'latexdoc': []}
89 fhooks = ['']
90
91 def fadd(line, s=fhooks):
92 s[0] = f'{s[0]}\n {line}'
93 doc = ['']
94
95 def dadd(line, s=doc):
96 s[0] = f'{s[0]}\n{line}'
97
98 usenames = getuseblocks(pymod)
99 for m in findf90modules(pymod):
100 sargs, fargs, efargs, modobjs, notvars, onlyvars = [], [], [], [], [
101 m['name']], []
102 sargsp = []
103 ifargs = []
104 mfargs = []
105 if hasbody(m):
106 for b in m['body']:
107 notvars.append(b['name'])
108 for n in m['vars'].keys():
109 var = m['vars'][n]
110
111 if (n not in notvars and isvariable(var)) and (not l_or(isintent_hide, isprivate)(var)):
112 onlyvars.append(n)
113 mfargs.append(n)
114 outmess(f"\t\tConstructing F90 module support for \"{m['name']}\"...\n")
115 if len(onlyvars) == 0 and len(notvars) == 1 and m['name'] in notvars:
116 outmess(f"\t\t\tSkipping {m['name']} since there are no public vars/func in this module...\n")
117 continue
118
119 # gh-25186
120 if m['name'] in usenames and containscommon(m):
121 outmess(f"\t\t\tSkipping {m['name']} since it is in 'use' and contains a common block...\n")
122 continue
123 # skip modules with derived types
124 if m['name'] in usenames and containsderivedtypes(m):
125 outmess(f"\t\t\tSkipping {m['name']} since it is in 'use' and contains a derived type...\n")
126 continue
127 if onlyvars:
128 outmess(f"\t\t Variables: {' '.join(onlyvars)}\n")
129 chooks = ['']
130
131 def cadd(line, s=chooks):
132 s[0] = f'{s[0]}\n{line}'
133 ihooks = ['']
134
135 def iadd(line, s=ihooks):
136 s[0] = f'{s[0]}\n{line}'
137
138 vrd = capi_maps.modsign2map(m)
139 cadd('static FortranDataDef f2py_%s_def[] = {' % (m['name']))
140 dadd('\\subsection{Fortran 90/95 module \\texttt{%s}}\n' % (m['name']))
141 if hasnote(m):
142 note = m['note']
143 if isinstance(note, list):
144 note = '\n'.join(note)
145 dadd(note)
146 if onlyvars:
147 dadd('\\begin{description}')
148 for n in onlyvars:
149 var = m['vars'][n]
150 modobjs.append(n)
151 ct = capi_maps.getctype(var)
152 at = capi_maps.c2capi_map[ct]
153 dm = capi_maps.getarrdims(n, var)
154 dms = dm['dims'].replace('*', '-1').strip()
155 dms = dms.replace(':', '-1').strip()
156 if not dms:
157 dms = '-1'
158 use_fgetdims2 = fgetdims2
159 cadd('\t{"%s",%s,{{%s}},%s, %s},' %
160 (undo_rmbadname1(n), dm['rank'], dms, at,
161 capi_maps.get_elsize(var)))
162 dadd('\\item[]{{}\\verb@%s@{}}' %
163 (capi_maps.getarrdocsign(n, var)))
164 if hasnote(var):
165 note = var['note']
166 if isinstance(note, list):
167 note = '\n'.join(note)
168 dadd(f'--- {note}')
169 if isallocatable(var):
170 fargs.append(f"f2py_{m['name']}_getdims_{n}")
171 efargs.append(fargs[-1])
172 sargs.append(
173 f'void (*{n})(int*,npy_intp*,void(*)(char*,npy_intp*),int*)')
174 sargsp.append('void (*)(int*,npy_intp*,void(*)(char*,npy_intp*),int*)')
175 iadd(f"\tf2py_{m['name']}_def[i_f2py++].func = {n};")
176 fadd(f'subroutine {fargs[-1]}(r,s,f2pysetdata,flag)')
177 fadd(f"use {m['name']}, only: d => {undo_rmbadname1(n)}\n")
178 fadd('integer flag\n')
179 fhooks[0] = fhooks[0] + fgetdims1
180 dms = range(1, int(dm['rank']) + 1)
181 fadd(' allocate(d(%s))\n' %
182 (','.join(['s(%s)' % i for i in dms])))
183 fhooks[0] = fhooks[0] + use_fgetdims2
184 fadd(f'end subroutine {fargs[-1]}')
185 else:
186 fargs.append(n)
187 sargs.append(f'char *{n}')
188 sargsp.append('char*')
189 iadd(f"\tf2py_{m['name']}_def[i_f2py++].data = {n};")
190 if onlyvars:
191 dadd('\\end{description}')
192 if hasbody(m):
193 for b in m['body']:
194 if not isroutine(b):
195 outmess("f90mod_rules.buildhooks:"
196 f" skipping {b['block']} {b['name']}\n")
197 continue
198 modobjs.append(f"{b['name']}()")
199 b['modulename'] = m['name']
200 api, wrap = rules.buildapi(b)
201 if isfunction(b):
202 fhooks[0] = fhooks[0] + wrap
203 fargs.append(f"f2pywrap_{m['name']}_{b['name']}")
204 ifargs.append(func2subr.createfuncwrapper(b, signature=1))
205 elif wrap:
206 fhooks[0] = fhooks[0] + wrap
207 fargs.append(f"f2pywrap_{m['name']}_{b['name']}")
208 ifargs.append(
209 func2subr.createsubrwrapper(b, signature=1))
210 else:
211 fargs.append(b['name'])
212 mfargs.append(fargs[-1])
213 api['externroutines'] = []
214 ar = applyrules(api, vrd)
215 ar['docs'] = []
216 ar['docshort'] = []
217 ret = dictappend(ret, ar)
218 cadd(('\t{"%s",-1,{{-1}},0,0,NULL,(void *)'
219 'f2py_rout_#modulename#_%s_%s,'
220 'doc_f2py_rout_#modulename#_%s_%s},')
221 % (b['name'], m['name'], b['name'], m['name'], b['name']))
222 sargs.append(f"char *{b['name']}")
223 sargsp.append('char *')
224 iadd(f"\tf2py_{m['name']}_def[i_f2py++].data = {b['name']};")
225 cadd('\t{NULL}\n};\n')
226 iadd('}')
227 ihooks[0] = 'static void f2py_setup_%s(%s) {\n\tint i_f2py=0;%s' % (
228 m['name'], ','.join(sargs), ihooks[0])
229 if '_' in m['name']:
230 F_FUNC = 'F_FUNC_US'
231 else:
232 F_FUNC = 'F_FUNC'
233 iadd('extern void %s(f2pyinit%s,F2PYINIT%s)(void (*)(%s));'
234 % (F_FUNC, m['name'], m['name'].upper(), ','.join(sargsp)))
235 iadd('static void f2py_init_%s(void) {' % (m['name']))
236 iadd('\t%s(f2pyinit%s,F2PYINIT%s)(f2py_setup_%s);'
237 % (F_FUNC, m['name'], m['name'].upper(), m['name']))
238 iadd('}\n')
239 ret['f90modhooks'] = ret['f90modhooks'] + chooks + ihooks
240 ret['initf90modhooks'] = ['\tPyDict_SetItemString(d, "%s", PyFortranObject_New(f2py_%s_def,f2py_init_%s));' % (
241 m['name'], m['name'], m['name'])] + ret['initf90modhooks']
242 fadd('')
243 fadd(f"subroutine f2pyinit{m['name']}(f2pysetupfunc)")
244 if mfargs:
245 for a in undo_rmbadname(mfargs):
246 fadd(f"use {m['name']}, only : {a}")
247 if ifargs:
248 fadd(' '.join(['interface'] + ifargs))
249 fadd('end interface')
250 fadd('external f2pysetupfunc')
251 if efargs:
252 for a in undo_rmbadname(efargs):
253 fadd(f'external {a}')
254 fadd(f"call f2pysetupfunc({','.join(undo_rmbadname(fargs))})")
255 fadd(f"end subroutine f2pyinit{m['name']}\n")
256
257 dadd('\n'.join(ret['latexdoc']).replace(
258 r'\subsection{', r'\subsubsection{'))
259
260 ret['latexdoc'] = []
261 ret['docs'].append(f"\"\t{m['name']} --- {','.join(undo_rmbadname(modobjs))}\"")
262
263 ret['routine_defs'] = ''
264 ret['doc'] = []
265 ret['docshort'] = []
266 ret['latexdoc'] = doc[0]
267 if len(ret['docs']) <= 1:
268 ret['docs'] = ''
269 return ret, fhooks[0]
270 