auxfuncs.py 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855
  1. #!/usr/bin/env python
  2. """
  3. Auxiliary functions for f2py2e.
  4. Copyright 1999,2000 Pearu Peterson all rights reserved,
  5. Pearu Peterson <pearu@ioc.ee>
  6. Permission to use, modify, and distribute this software is given under the
  7. terms of the NumPy (BSD style) LICENSE.
  8. NO WARRANTY IS EXPRESSED OR IMPLIED. USE AT YOUR OWN RISK.
  9. $Date: 2005/07/24 19:01:55 $
  10. Pearu Peterson
  11. """
  12. from __future__ import division, absolute_import, print_function
  13. import pprint
  14. import sys
  15. import types
  16. from functools import reduce
  17. from . import __version__
  18. from . import cfuncs
  19. __all__ = [
  20. 'applyrules', 'debugcapi', 'dictappend', 'errmess', 'gentitle',
  21. 'getargs2', 'getcallprotoargument', 'getcallstatement',
  22. 'getfortranname', 'getpymethoddef', 'getrestdoc', 'getusercode',
  23. 'getusercode1', 'hasbody', 'hascallstatement', 'hascommon',
  24. 'hasexternals', 'hasinitvalue', 'hasnote', 'hasresultnote',
  25. 'isallocatable', 'isarray', 'isarrayofstrings', 'iscomplex',
  26. 'iscomplexarray', 'iscomplexfunction', 'iscomplexfunction_warn',
  27. 'isdouble', 'isdummyroutine', 'isexternal', 'isfunction',
  28. 'isfunction_wrap', 'isint1array', 'isinteger', 'isintent_aux',
  29. 'isintent_c', 'isintent_callback', 'isintent_copy', 'isintent_dict',
  30. 'isintent_hide', 'isintent_in', 'isintent_inout', 'isintent_inplace',
  31. 'isintent_nothide', 'isintent_out', 'isintent_overwrite', 'islogical',
  32. 'islogicalfunction', 'islong_complex', 'islong_double',
  33. 'islong_doublefunction', 'islong_long', 'islong_longfunction',
  34. 'ismodule', 'ismoduleroutine', 'isoptional', 'isprivate', 'isrequired',
  35. 'isroutine', 'isscalar', 'issigned_long_longarray', 'isstring',
  36. 'isstringarray', 'isstringfunction', 'issubroutine',
  37. 'issubroutine_wrap', 'isthreadsafe', 'isunsigned', 'isunsigned_char',
  38. 'isunsigned_chararray', 'isunsigned_long_long',
  39. 'isunsigned_long_longarray', 'isunsigned_short',
  40. 'isunsigned_shortarray', 'l_and', 'l_not', 'l_or', 'outmess',
  41. 'replace', 'show', 'stripcomma', 'throw_error',
  42. ]
  43. f2py_version = __version__.version
  44. errmess = sys.stderr.write
  45. show = pprint.pprint
  46. options = {}
  47. debugoptions = []
  48. wrapfuncs = 1
  49. def outmess(t):
  50. if options.get('verbose', 1):
  51. sys.stdout.write(t)
  52. def debugcapi(var):
  53. return 'capi' in debugoptions
  54. def _isstring(var):
  55. return 'typespec' in var and var['typespec'] == 'character' and \
  56. not isexternal(var)
  57. def isstring(var):
  58. return _isstring(var) and not isarray(var)
  59. def ischaracter(var):
  60. return isstring(var) and 'charselector' not in var
  61. def isstringarray(var):
  62. return isarray(var) and _isstring(var)
  63. def isarrayofstrings(var):
  64. # leaving out '*' for now so that `character*(*) a(m)` and `character
  65. # a(m,*)` are treated differently. Luckily `character**` is illegal.
  66. return isstringarray(var) and var['dimension'][-1] == '(*)'
  67. def isarray(var):
  68. return 'dimension' in var and not isexternal(var)
  69. def isscalar(var):
  70. return not (isarray(var) or isstring(var) or isexternal(var))
  71. def iscomplex(var):
  72. return isscalar(var) and \
  73. var.get('typespec') in ['complex', 'double complex']
  74. def islogical(var):
  75. return isscalar(var) and var.get('typespec') == 'logical'
  76. def isinteger(var):
  77. return isscalar(var) and var.get('typespec') == 'integer'
  78. def isreal(var):
  79. return isscalar(var) and var.get('typespec') == 'real'
  80. def get_kind(var):
  81. try:
  82. return var['kindselector']['*']
  83. except KeyError:
  84. try:
  85. return var['kindselector']['kind']
  86. except KeyError:
  87. pass
  88. def islong_long(var):
  89. if not isscalar(var):
  90. return 0
  91. if var.get('typespec') not in ['integer', 'logical']:
  92. return 0
  93. return get_kind(var) == '8'
  94. def isunsigned_char(var):
  95. if not isscalar(var):
  96. return 0
  97. if var.get('typespec') != 'integer':
  98. return 0
  99. return get_kind(var) == '-1'
  100. def isunsigned_short(var):
  101. if not isscalar(var):
  102. return 0
  103. if var.get('typespec') != 'integer':
  104. return 0
  105. return get_kind(var) == '-2'
  106. def isunsigned(var):
  107. if not isscalar(var):
  108. return 0
  109. if var.get('typespec') != 'integer':
  110. return 0
  111. return get_kind(var) == '-4'
  112. def isunsigned_long_long(var):
  113. if not isscalar(var):
  114. return 0
  115. if var.get('typespec') != 'integer':
  116. return 0
  117. return get_kind(var) == '-8'
  118. def isdouble(var):
  119. if not isscalar(var):
  120. return 0
  121. if not var.get('typespec') == 'real':
  122. return 0
  123. return get_kind(var) == '8'
  124. def islong_double(var):
  125. if not isscalar(var):
  126. return 0
  127. if not var.get('typespec') == 'real':
  128. return 0
  129. return get_kind(var) == '16'
  130. def islong_complex(var):
  131. if not iscomplex(var):
  132. return 0
  133. return get_kind(var) == '32'
  134. def iscomplexarray(var):
  135. return isarray(var) and \
  136. var.get('typespec') in ['complex', 'double complex']
  137. def isint1array(var):
  138. return isarray(var) and var.get('typespec') == 'integer' \
  139. and get_kind(var) == '1'
  140. def isunsigned_chararray(var):
  141. return isarray(var) and var.get('typespec') in ['integer', 'logical']\
  142. and get_kind(var) == '-1'
  143. def isunsigned_shortarray(var):
  144. return isarray(var) and var.get('typespec') in ['integer', 'logical']\
  145. and get_kind(var) == '-2'
  146. def isunsignedarray(var):
  147. return isarray(var) and var.get('typespec') in ['integer', 'logical']\
  148. and get_kind(var) == '-4'
  149. def isunsigned_long_longarray(var):
  150. return isarray(var) and var.get('typespec') in ['integer', 'logical']\
  151. and get_kind(var) == '-8'
  152. def issigned_chararray(var):
  153. return isarray(var) and var.get('typespec') in ['integer', 'logical']\
  154. and get_kind(var) == '1'
  155. def issigned_shortarray(var):
  156. return isarray(var) and var.get('typespec') in ['integer', 'logical']\
  157. and get_kind(var) == '2'
  158. def issigned_array(var):
  159. return isarray(var) and var.get('typespec') in ['integer', 'logical']\
  160. and get_kind(var) == '4'
  161. def issigned_long_longarray(var):
  162. return isarray(var) and var.get('typespec') in ['integer', 'logical']\
  163. and get_kind(var) == '8'
  164. def isallocatable(var):
  165. return 'attrspec' in var and 'allocatable' in var['attrspec']
  166. def ismutable(var):
  167. return not ('dimension' not in var or isstring(var))
  168. def ismoduleroutine(rout):
  169. return 'modulename' in rout
  170. def ismodule(rout):
  171. return 'block' in rout and 'module' == rout['block']
  172. def isfunction(rout):
  173. return 'block' in rout and 'function' == rout['block']
  174. def isfunction_wrap(rout):
  175. if isintent_c(rout):
  176. return 0
  177. return wrapfuncs and isfunction(rout) and (not isexternal(rout))
  178. def issubroutine(rout):
  179. return 'block' in rout and 'subroutine' == rout['block']
  180. def issubroutine_wrap(rout):
  181. if isintent_c(rout):
  182. return 0
  183. return issubroutine(rout) and hasassumedshape(rout)
  184. def hasassumedshape(rout):
  185. if rout.get('hasassumedshape'):
  186. return True
  187. for a in rout['args']:
  188. for d in rout['vars'].get(a, {}).get('dimension', []):
  189. if d == ':':
  190. rout['hasassumedshape'] = True
  191. return True
  192. return False
  193. def isroutine(rout):
  194. return isfunction(rout) or issubroutine(rout)
  195. def islogicalfunction(rout):
  196. if not isfunction(rout):
  197. return 0
  198. if 'result' in rout:
  199. a = rout['result']
  200. else:
  201. a = rout['name']
  202. if a in rout['vars']:
  203. return islogical(rout['vars'][a])
  204. return 0
  205. def islong_longfunction(rout):
  206. if not isfunction(rout):
  207. return 0
  208. if 'result' in rout:
  209. a = rout['result']
  210. else:
  211. a = rout['name']
  212. if a in rout['vars']:
  213. return islong_long(rout['vars'][a])
  214. return 0
  215. def islong_doublefunction(rout):
  216. if not isfunction(rout):
  217. return 0
  218. if 'result' in rout:
  219. a = rout['result']
  220. else:
  221. a = rout['name']
  222. if a in rout['vars']:
  223. return islong_double(rout['vars'][a])
  224. return 0
  225. def iscomplexfunction(rout):
  226. if not isfunction(rout):
  227. return 0
  228. if 'result' in rout:
  229. a = rout['result']
  230. else:
  231. a = rout['name']
  232. if a in rout['vars']:
  233. return iscomplex(rout['vars'][a])
  234. return 0
  235. def iscomplexfunction_warn(rout):
  236. if iscomplexfunction(rout):
  237. outmess("""\
  238. **************************************************************
  239. Warning: code with a function returning complex value
  240. may not work correctly with your Fortran compiler.
  241. Run the following test before using it in your applications:
  242. $(f2py install dir)/test-site/{b/runme_scalar,e/runme}
  243. When using GNU gcc/g77 compilers, codes should work correctly.
  244. **************************************************************\n""")
  245. return 1
  246. return 0
  247. def isstringfunction(rout):
  248. if not isfunction(rout):
  249. return 0
  250. if 'result' in rout:
  251. a = rout['result']
  252. else:
  253. a = rout['name']
  254. if a in rout['vars']:
  255. return isstring(rout['vars'][a])
  256. return 0
  257. def hasexternals(rout):
  258. return 'externals' in rout and rout['externals']
  259. def isthreadsafe(rout):
  260. return 'f2pyenhancements' in rout and \
  261. 'threadsafe' in rout['f2pyenhancements']
  262. def hasvariables(rout):
  263. return 'vars' in rout and rout['vars']
  264. def isoptional(var):
  265. return ('attrspec' in var and 'optional' in var['attrspec'] and
  266. 'required' not in var['attrspec']) and isintent_nothide(var)
  267. def isexternal(var):
  268. return 'attrspec' in var and 'external' in var['attrspec']
  269. def isrequired(var):
  270. return not isoptional(var) and isintent_nothide(var)
  271. def isintent_in(var):
  272. if 'intent' not in var:
  273. return 1
  274. if 'hide' in var['intent']:
  275. return 0
  276. if 'inplace' in var['intent']:
  277. return 0
  278. if 'in' in var['intent']:
  279. return 1
  280. if 'out' in var['intent']:
  281. return 0
  282. if 'inout' in var['intent']:
  283. return 0
  284. if 'outin' in var['intent']:
  285. return 0
  286. return 1
  287. def isintent_inout(var):
  288. return ('intent' in var and ('inout' in var['intent'] or
  289. 'outin' in var['intent']) and 'in' not in var['intent'] and
  290. 'hide' not in var['intent'] and 'inplace' not in var['intent'])
  291. def isintent_out(var):
  292. return 'out' in var.get('intent', [])
  293. def isintent_hide(var):
  294. return ('intent' in var and ('hide' in var['intent'] or
  295. ('out' in var['intent'] and 'in' not in var['intent'] and
  296. (not l_or(isintent_inout, isintent_inplace)(var)))))
  297. def isintent_nothide(var):
  298. return not isintent_hide(var)
  299. def isintent_c(var):
  300. return 'c' in var.get('intent', [])
  301. def isintent_cache(var):
  302. return 'cache' in var.get('intent', [])
  303. def isintent_copy(var):
  304. return 'copy' in var.get('intent', [])
  305. def isintent_overwrite(var):
  306. return 'overwrite' in var.get('intent', [])
  307. def isintent_callback(var):
  308. return 'callback' in var.get('intent', [])
  309. def isintent_inplace(var):
  310. return 'inplace' in var.get('intent', [])
  311. def isintent_aux(var):
  312. return 'aux' in var.get('intent', [])
  313. def isintent_aligned4(var):
  314. return 'aligned4' in var.get('intent', [])
  315. def isintent_aligned8(var):
  316. return 'aligned8' in var.get('intent', [])
  317. def isintent_aligned16(var):
  318. return 'aligned16' in var.get('intent', [])
  319. isintent_dict = {isintent_in: 'INTENT_IN', isintent_inout: 'INTENT_INOUT',
  320. isintent_out: 'INTENT_OUT', isintent_hide: 'INTENT_HIDE',
  321. isintent_cache: 'INTENT_CACHE',
  322. isintent_c: 'INTENT_C', isoptional: 'OPTIONAL',
  323. isintent_inplace: 'INTENT_INPLACE',
  324. isintent_aligned4: 'INTENT_ALIGNED4',
  325. isintent_aligned8: 'INTENT_ALIGNED8',
  326. isintent_aligned16: 'INTENT_ALIGNED16',
  327. }
  328. def isprivate(var):
  329. return 'attrspec' in var and 'private' in var['attrspec']
  330. def hasinitvalue(var):
  331. return '=' in var
  332. def hasinitvalueasstring(var):
  333. if not hasinitvalue(var):
  334. return 0
  335. return var['='][0] in ['"', "'"]
  336. def hasnote(var):
  337. return 'note' in var
  338. def hasresultnote(rout):
  339. if not isfunction(rout):
  340. return 0
  341. if 'result' in rout:
  342. a = rout['result']
  343. else:
  344. a = rout['name']
  345. if a in rout['vars']:
  346. return hasnote(rout['vars'][a])
  347. return 0
  348. def hascommon(rout):
  349. return 'common' in rout
  350. def containscommon(rout):
  351. if hascommon(rout):
  352. return 1
  353. if hasbody(rout):
  354. for b in rout['body']:
  355. if containscommon(b):
  356. return 1
  357. return 0
  358. def containsmodule(block):
  359. if ismodule(block):
  360. return 1
  361. if not hasbody(block):
  362. return 0
  363. for b in block['body']:
  364. if containsmodule(b):
  365. return 1
  366. return 0
  367. def hasbody(rout):
  368. return 'body' in rout
  369. def hascallstatement(rout):
  370. return getcallstatement(rout) is not None
  371. def istrue(var):
  372. return 1
  373. def isfalse(var):
  374. return 0
  375. class F2PYError(Exception):
  376. pass
  377. class throw_error(object):
  378. def __init__(self, mess):
  379. self.mess = mess
  380. def __call__(self, var):
  381. mess = '\n\n var = %s\n Message: %s\n' % (var, self.mess)
  382. raise F2PYError(mess)
  383. def l_and(*f):
  384. l, l2 = 'lambda v', []
  385. for i in range(len(f)):
  386. l = '%s,f%d=f[%d]' % (l, i, i)
  387. l2.append('f%d(v)' % (i))
  388. return eval('%s:%s' % (l, ' and '.join(l2)))
  389. def l_or(*f):
  390. l, l2 = 'lambda v', []
  391. for i in range(len(f)):
  392. l = '%s,f%d=f[%d]' % (l, i, i)
  393. l2.append('f%d(v)' % (i))
  394. return eval('%s:%s' % (l, ' or '.join(l2)))
  395. def l_not(f):
  396. return eval('lambda v,f=f:not f(v)')
  397. def isdummyroutine(rout):
  398. try:
  399. return rout['f2pyenhancements']['fortranname'] == ''
  400. except KeyError:
  401. return 0
  402. def getfortranname(rout):
  403. try:
  404. name = rout['f2pyenhancements']['fortranname']
  405. if name == '':
  406. raise KeyError
  407. if not name:
  408. errmess('Failed to use fortranname from %s\n' %
  409. (rout['f2pyenhancements']))
  410. raise KeyError
  411. except KeyError:
  412. name = rout['name']
  413. return name
  414. def getmultilineblock(rout, blockname, comment=1, counter=0):
  415. try:
  416. r = rout['f2pyenhancements'].get(blockname)
  417. except KeyError:
  418. return
  419. if not r:
  420. return
  421. if counter > 0 and isinstance(r, str):
  422. return
  423. if isinstance(r, list):
  424. if counter >= len(r):
  425. return
  426. r = r[counter]
  427. if r[:3] == "'''":
  428. if comment:
  429. r = '\t/* start ' + blockname + \
  430. ' multiline (' + repr(counter) + ') */\n' + r[3:]
  431. else:
  432. r = r[3:]
  433. if r[-3:] == "'''":
  434. if comment:
  435. r = r[:-3] + '\n\t/* end multiline (' + repr(counter) + ')*/'
  436. else:
  437. r = r[:-3]
  438. else:
  439. errmess("%s multiline block should end with `'''`: %s\n"
  440. % (blockname, repr(r)))
  441. return r
  442. def getcallstatement(rout):
  443. return getmultilineblock(rout, 'callstatement')
  444. def getcallprotoargument(rout, cb_map={}):
  445. r = getmultilineblock(rout, 'callprotoargument', comment=0)
  446. if r:
  447. return r
  448. if hascallstatement(rout):
  449. outmess(
  450. 'warning: callstatement is defined without callprotoargument\n')
  451. return
  452. from .capi_maps import getctype
  453. arg_types, arg_types2 = [], []
  454. if l_and(isstringfunction, l_not(isfunction_wrap))(rout):
  455. arg_types.extend(['char*', 'size_t'])
  456. for n in rout['args']:
  457. var = rout['vars'][n]
  458. if isintent_callback(var):
  459. continue
  460. if n in cb_map:
  461. ctype = cb_map[n] + '_typedef'
  462. else:
  463. ctype = getctype(var)
  464. if l_and(isintent_c, l_or(isscalar, iscomplex))(var):
  465. pass
  466. elif isstring(var):
  467. pass
  468. else:
  469. ctype = ctype + '*'
  470. if isstring(var) or isarrayofstrings(var):
  471. arg_types2.append('size_t')
  472. arg_types.append(ctype)
  473. proto_args = ','.join(arg_types + arg_types2)
  474. if not proto_args:
  475. proto_args = 'void'
  476. return proto_args
  477. def getusercode(rout):
  478. return getmultilineblock(rout, 'usercode')
  479. def getusercode1(rout):
  480. return getmultilineblock(rout, 'usercode', counter=1)
  481. def getpymethoddef(rout):
  482. return getmultilineblock(rout, 'pymethoddef')
  483. def getargs(rout):
  484. sortargs, args = [], []
  485. if 'args' in rout:
  486. args = rout['args']
  487. if 'sortvars' in rout:
  488. for a in rout['sortvars']:
  489. if a in args:
  490. sortargs.append(a)
  491. for a in args:
  492. if a not in sortargs:
  493. sortargs.append(a)
  494. else:
  495. sortargs = rout['args']
  496. return args, sortargs
  497. def getargs2(rout):
  498. sortargs, args = [], rout.get('args', [])
  499. auxvars = [a for a in rout['vars'].keys() if isintent_aux(rout['vars'][a])
  500. and a not in args]
  501. args = auxvars + args
  502. if 'sortvars' in rout:
  503. for a in rout['sortvars']:
  504. if a in args:
  505. sortargs.append(a)
  506. for a in args:
  507. if a not in sortargs:
  508. sortargs.append(a)
  509. else:
  510. sortargs = auxvars + rout['args']
  511. return args, sortargs
  512. def getrestdoc(rout):
  513. if 'f2pymultilines' not in rout:
  514. return None
  515. k = None
  516. if rout['block'] == 'python module':
  517. k = rout['block'], rout['name']
  518. return rout['f2pymultilines'].get(k, None)
  519. def gentitle(name):
  520. l = (80 - len(name) - 6) // 2
  521. return '/*%s %s %s*/' % (l * '*', name, l * '*')
  522. def flatlist(l):
  523. if isinstance(l, list):
  524. return reduce(lambda x, y, f=flatlist: x + f(y), l, [])
  525. return [l]
  526. def stripcomma(s):
  527. if s and s[-1] == ',':
  528. return s[:-1]
  529. return s
  530. def replace(str, d, defaultsep=''):
  531. if isinstance(d, list):
  532. return [replace(str, _m, defaultsep) for _m in d]
  533. if isinstance(str, list):
  534. return [replace(_m, d, defaultsep) for _m in str]
  535. for k in 2 * list(d.keys()):
  536. if k == 'separatorsfor':
  537. continue
  538. if 'separatorsfor' in d and k in d['separatorsfor']:
  539. sep = d['separatorsfor'][k]
  540. else:
  541. sep = defaultsep
  542. if isinstance(d[k], list):
  543. str = str.replace('#%s#' % (k), sep.join(flatlist(d[k])))
  544. else:
  545. str = str.replace('#%s#' % (k), d[k])
  546. return str
  547. def dictappend(rd, ar):
  548. if isinstance(ar, list):
  549. for a in ar:
  550. rd = dictappend(rd, a)
  551. return rd
  552. for k in ar.keys():
  553. if k[0] == '_':
  554. continue
  555. if k in rd:
  556. if isinstance(rd[k], str):
  557. rd[k] = [rd[k]]
  558. if isinstance(rd[k], list):
  559. if isinstance(ar[k], list):
  560. rd[k] = rd[k] + ar[k]
  561. else:
  562. rd[k].append(ar[k])
  563. elif isinstance(rd[k], dict):
  564. if isinstance(ar[k], dict):
  565. if k == 'separatorsfor':
  566. for k1 in ar[k].keys():
  567. if k1 not in rd[k]:
  568. rd[k][k1] = ar[k][k1]
  569. else:
  570. rd[k] = dictappend(rd[k], ar[k])
  571. else:
  572. rd[k] = ar[k]
  573. return rd
  574. def applyrules(rules, d, var={}):
  575. ret = {}
  576. if isinstance(rules, list):
  577. for r in rules:
  578. rr = applyrules(r, d, var)
  579. ret = dictappend(ret, rr)
  580. if '_break' in rr:
  581. break
  582. return ret
  583. if '_check' in rules and (not rules['_check'](var)):
  584. return ret
  585. if 'need' in rules:
  586. res = applyrules({'needs': rules['need']}, d, var)
  587. if 'needs' in res:
  588. cfuncs.append_needs(res['needs'])
  589. for k in rules.keys():
  590. if k == 'separatorsfor':
  591. ret[k] = rules[k]
  592. continue
  593. if isinstance(rules[k], str):
  594. ret[k] = replace(rules[k], d)
  595. elif isinstance(rules[k], list):
  596. ret[k] = []
  597. for i in rules[k]:
  598. ar = applyrules({k: i}, d, var)
  599. if k in ar:
  600. ret[k].append(ar[k])
  601. elif k[0] == '_':
  602. continue
  603. elif isinstance(rules[k], dict):
  604. ret[k] = []
  605. for k1 in rules[k].keys():
  606. if isinstance(k1, types.FunctionType) and k1(var):
  607. if isinstance(rules[k][k1], list):
  608. for i in rules[k][k1]:
  609. if isinstance(i, dict):
  610. res = applyrules({'supertext': i}, d, var)
  611. if 'supertext' in res:
  612. i = res['supertext']
  613. else:
  614. i = ''
  615. ret[k].append(replace(i, d))
  616. else:
  617. i = rules[k][k1]
  618. if isinstance(i, dict):
  619. res = applyrules({'supertext': i}, d)
  620. if 'supertext' in res:
  621. i = res['supertext']
  622. else:
  623. i = ''
  624. ret[k].append(replace(i, d))
  625. else:
  626. errmess('applyrules: ignoring rule %s.\n' % repr(rules[k]))
  627. if isinstance(ret[k], list):
  628. if len(ret[k]) == 1:
  629. ret[k] = ret[k][0]
  630. if ret[k] == []:
  631. del ret[k]
  632. return ret