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