f90mod_rules.py 9.6 KB

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