Package ComboCode :: Package cc :: Package ivs :: Package sigproc :: Package lmfit :: Module asteval
[hide private]
[frames] | no frames]

Source Code for Module ComboCode.cc.ivs.sigproc.lmfit.asteval

  1  """ 
  2  Safe(ish) evaluator of python expressions, using ast module. 
  3  The emphasis here is on mathematical expressions, and so 
  4  numpy functions are imported if available and used. 
  5   
  6  Symbols are held in the Interpreter symtable -- a simple 
  7  dictionary supporting a simple, flat namespace. 
  8   
  9  Expressions can be compiled into ast node and then evaluated 
 10  later, using the current values in the 
 11  """ 
 12  from __future__ import division, print_function 
 13  from sys import exc_info, stdout, version_info 
 14  import ast 
 15  import math 
 16  from .astutils import (FROM_PY, FROM_MATH, FROM_NUMPY, 
 17                         NUMPY_RENAMES, ExceptionHolder) 
 18   
 19  from .parameter import isParameter, valid_symbol_name 
 20   
 21  HAS_NUMPY = False 
 22  try: 
 23      import numpy 
 24      HAS_NUMPY = True 
 25  except ImportError: 
 26      print("Warning: numpy not available... functionality will be limited.") 
 27   
 28   
 29  OPERATORS = { 
 30      ast.Add:    lambda a, b: b.__radd__(a) if isParameter(b) else a + b, 
 31      ast.Sub:    lambda a, b: b.__rsub__(a) if isParameter(b) else a - b, 
 32      ast.Mult:   lambda a, b: b.__rmul__(a) if isParameter(b) else a * b, 
 33      ast.Div:    lambda a, b: b.__rdiv__(a) if isParameter(b) else a / b, 
 34      ast.FloorDiv: lambda a, b: b.__rfloordiv__(a) if isParameter(b) else a // b, 
 35      ast.Mod:    lambda a, b: b.__rmod__(a) if isParameter(b) else a % b, 
 36      ast.Pow:    lambda a, b: b.__rpow__(a) if isParameter(b) else a ** b, 
 37      ast.Eq:     lambda a, b: b.__eq__(a)  if isParameter(b) else a == b, 
 38      ast.Gt:     lambda a, b: b.__le__(a) if isParameter(b) else a > b, 
 39      ast.GtE:    lambda a, b: b.__lt__(a) if isParameter(b) else a >= b, 
 40      ast.Lt:     lambda a, b: b.__ge__(a) if isParameter(b) else a < b, 
 41      ast.LtE:    lambda a, b: b.__gt__(a) if isParameter(b) else a <= b, 
 42      ast.NotEq:  lambda a, b: b.__ne__(a) if isParameter(b) else a != b, 
 43      ast.Is:     lambda a, b: a is b, 
 44      ast.IsNot:  lambda a, b: a is not b, 
 45      ast.In:     lambda a, b: a in b, 
 46      ast.NotIn:  lambda a, b: a not in b, 
 47      ast.BitAnd: lambda a, b: a & b, 
 48      ast.BitOr:  lambda a, b: a | b, 
 49      ast.BitXor: lambda a, b: a ^ b, 
 50      ast.LShift: lambda a, b: a << b, 
 51      ast.RShift: lambda a, b: a >> b, 
 52      ast.And:    lambda a, b: a and b, 
 53      ast.Or:     lambda a, b: a or b, 
 54      ast.Invert: lambda a: ~a, 
 55      ast.Not:    lambda a: not a, 
 56      ast.UAdd:   lambda a: +a, 
 57      ast.USub:   lambda a: -a} 
 58   
59 -def op2func(op):
60 "return function for operator nodes" 61 return OPERATORS[op.__class__]
62 63 __version__ = '0.3.1' 64 65 # holder for 'returned None' from Larch procedure
66 -class Empty:
67 """basic empty containter"""
68 - def __nonzero__(self):
69 return False
70 71 ReturnedNone = Empty() 72
73 -class Interpreter:
74 """mathematical expression compiler and interpreter. 75 76 This module compiles expressions and statements to AST representation, 77 using python's ast module, and then executes the AST representation 78 using a dictionary of named object (variable, functions). 79 80 This then gives a restricted version of Python, being a procedural 81 language (though working on Python objects) with a simplified, flat 82 namespace (this is overcome in related implementaions). The program 83 syntax here is expected to be valid Python. 84 85 The following Python syntax elements are not supported: 86 Import, Exec, Lambda, Class, Global, Generators, 87 Yield, Decorators, Finally for Try-Except 88 89 Many parts of Python syntax are supported, including: 90 advanced slicing: a[::-1], array[-3:, :, ::2] 91 if-expressions: out = one_thing if TEST else other 92 list comprehension 93 for-loops, while-loops 94 if-then-elif-else conditionals 95 try-except (but not the 'finally' variant...) 96 function definitions with def 97 """ 98 99 supported_nodes = ('arg', 'assert', 'assign', 'attribute', 'augassign', 100 'binop', 'boolop', 'break', 'call', 'compare', 101 'continue', 'delete', 'dict', 'ellipsis', 102 'excepthandler', 'expr', 'extslice', 'for', 103 'functiondef', 'if', 'ifexp', 'index', 'interrupt', 104 'list', 'listcomp', 'module', 'name', 'num', 'pass', 105 'print', 'raise', 'repr', 'return', 'slice', 'str', 106 'subscript', 'tryexcept', 'tuple', 'unaryop', 107 'while') 108
109 - def __init__(self, symtable=None, writer=None, use_numpy=True):
110 self.writer = writer or stdout 111 112 if symtable is None: 113 symtable = {} 114 self.symtable = symtable 115 self._interrupt = None 116 self.error = [] 117 self.expr = None 118 self.retval = None 119 self.errmsg = None 120 self.lineno = 0 121 global HAS_NUMPY 122 if not use_numpy: 123 HAS_NUMPY = False 124 125 symtable['print'] = self._printer 126 for sym in FROM_PY: 127 if sym in __builtins__: 128 symtable[sym] = __builtins__[sym] 129 for sym in FROM_MATH: 130 if hasattr(math, sym): 131 symtable[sym] = getattr(math, sym) 132 133 if HAS_NUMPY: 134 for sym in FROM_NUMPY: 135 if hasattr(numpy, sym): 136 symtable[sym] = getattr(numpy, sym) 137 for name, sym in NUMPY_RENAMES.items(): 138 if hasattr(numpy, sym): 139 symtable[name] = getattr(numpy, sym) 140 141 self.node_handlers = dict(((node, getattr(self, "on_%s" % node)) 142 for node in self.supported_nodes))
143
144 - def unimplemented(self, node):
145 "unimplemented nodes" 146 self.raise_exception(node, exc=NotImplementedError, 147 msg="'%s' not supported" % (node.__class__.__name__))
148
149 - def raise_exception(self, node, exc=None, msg='', expr=None, 150 lineno=None):
151 "add an exception" 152 if self.error is None: 153 self.error = [] 154 if expr is None: 155 expr = self.expr 156 157 if len(self.error) > 0 and not isinstance(node, ast.Module): 158 msg = '%s' % msg 159 if self.errmsg is None: 160 self.errmsg = msg 161 err = ExceptionHolder(node, exc=exc, msg=self.errmsg, 162 expr=expr, lineno=lineno) 163 self._interrupt = ast.Break() 164 self.error.append(err) 165 raise RuntimeError(err.msg)
166 167 # main entry point for Ast node evaluation 168 # parse: text of statements -> ast 169 # run: ast -> result 170 # eval: string statement -> result = run(parse(statement))
171 - def parse(self, text):
172 """parse statement/expression to Ast representation""" 173 self.expr = text 174 try: 175 return ast.parse(text) 176 except: 177 self.raise_exception(None, exc=SyntaxError, 178 msg='Syntax Error', expr=text)
179
180 - def run(self, node, expr=None, lineno=None, with_raise=True):
181 """executes parsed Ast representation for an expression""" 182 # Note: keep the 'node is None' test: internal code here may run 183 # run(None) and expect a None in return. 184 if len(self.error)>0: 185 return 186 if node is None: 187 return None 188 if isinstance(node, str): 189 node = self.parse(node) 190 if lineno is not None: 191 self.lineno = lineno 192 193 if expr is not None: 194 self.expr = expr 195 196 # get handler for this node: 197 # on_xxx with handle nodes of type 'xxx', etc 198 try: 199 handler = self.node_handlers[node.__class__.__name__.lower()] 200 except KeyError: 201 return self.unimplemented(node) 202 203 # run the handler: this will likely generate 204 # recursive calls into this run method. 205 try: 206 ret = handler(node) 207 if isinstance(ret, enumerate): 208 ret = list(ret) 209 return ret 210 except: 211 if with_raise: 212 self.raise_exception(node, expr=expr)
213
214 - def __call__(self, expr, **kw):
215 return self.eval(expr, **kw)
216
217 - def eval(self, expr, lineno=0, show_errors=True):
218 """evaluates a single statement""" 219 self.lineno = lineno 220 self.errmsg = None 221 self.error = [] 222 try: 223 node = self.parse(expr) 224 except RuntimeError: 225 errmsg = exc_info()[1] 226 if len(self.error) > 0: 227 errmsg = "\n".join(self.error[0].get_error()) 228 if not show_errors: 229 raise RuntimeError(errmsg) 230 print(errmsg, file=self.writer) 231 return 232 try: 233 return self.run(node, expr=expr, lineno=lineno) 234 except RuntimeError: 235 errmsg = exc_info()[1] 236 if len(self.error) > 0: 237 errmsg = "\n".join(self.error[0].get_error()) 238 if not show_errors: 239 raise RuntimeError(errmsg) 240 print(errmsg, file=self.writer) 241 return
242
243 - def dump(self, node, **kw):
244 "simple ast dumper" 245 return ast.dump(node, **kw)
246 247 # handlers for ast components
248 - def on_expr(self, node):
249 "expression" 250 return self.run(node.value) # ('value',)
251
252 - def on_index(self, node):
253 "index" 254 return self.run(node.value) # ('value',)
255
256 - def on_return(self, node): # ('value',)
257 "return statement: look for None, return special sentinal" 258 self.retval = self.run(node.value) 259 if self.retval is None: 260 self.retval = ReturnedNone 261 return
262
263 - def on_repr(self, node):
264 "repr " 265 return repr(self.run(node.value)) # ('value',)
266
267 - def on_module(self, node): # ():('body',)
268 "module def" 269 out = None 270 for tnode in node.body: 271 out = self.run(tnode) 272 return out 273
274 - def on_pass(self, node):
275 "pass statement" 276 return None # ()
277
278 - def on_ellipsis(self, node):
279 "ellipses" 280 return Ellipsis
281 282 # for break and continue: set the instance variable _interrupt
283 - def on_interrupt(self, node): # ()
284 "interrupt handler" 285 self._interrupt = node 286 return node 287
288 - def on_break(self, node):
289 "break" 290 return self.on_interrupt(node)
291
292 - def on_continue(self, node):
293 "continue" 294 return self.on_interrupt(node)
295
296 - def on_assert(self, node): # ('test', 'msg')
297 "assert statement" 298 if not self.run(node.test): 299 self.raise_exception(node, exc=AssertionError, msg=node.msg) 300 return True 301
302 - def on_list(self, node): # ('elt', 'ctx')
303 "list" 304 return [self.run(e) for e in node.elts] 305
306 - def on_tuple(self, node): # ('elts', 'ctx')
307 "tuple" 308 return tuple(self.on_list(node)) 309
310 - def on_dict(self, node): # ('keys', 'values')
311 "dictionary" 312 return dict([(self.run(k), self.run(v)) for k, v in \ 313 zip(node.keys, node.values)]) 314
315 - def on_num(self, node): # ('n',)
316 'return number' 317 return node.n 318
319 - def on_str(self, node): # ('s',)
320 'return string' 321 return node.s 322
323 - def on_name(self, node): # ('id', 'ctx')
324 """ Name node """ 325 ctx = node.ctx.__class__ 326 if ctx in (ast.Param, ast.Del): 327 return str(node.id) 328 else: 329 if node.id in self.symtable: 330 return self.symtable[node.id] 331 else: 332 msg = "name '%s' is not defined" % node.id 333 self.raise_exception(node, exc=NameError, msg=msg) 334 335
336 - def node_assign(self, node, val):
337 """here we assign a value (not the node.value object) to a node 338 this is used by on_assign, but also by for, list comprehension, etc. 339 """ 340 if node.__class__ == ast.Name: 341 if not valid_symbol_name(node.id): 342 errmsg = "invalid symbol name (reserved word?) %s" % node.id 343 self.raise_exception(node, exc=NameError, msg=errmsg) 344 sym = self.symtable[node.id] = val 345 elif node.__class__ == ast.Attribute: 346 if node.ctx.__class__ == ast.Load: 347 msg = "cannot assign to attribute %s" % node.attr 348 self.raise_exception(node, exc=AttributeError, msg=msg) 349 350 setattr(self.run(node.value), node.attr, val) 351 352 elif node.__class__ == ast.Subscript: 353 sym = self.run(node.value) 354 xslice = self.run(node.slice) 355 if isinstance(node.slice, ast.Index): 356 sym[xslice] = val 357 elif isinstance(node.slice, ast.Slice): 358 sym[slice(xslice.start, xslice.stop)] = val 359 elif isinstance(node.slice, ast.ExtSlice): 360 sym[(xslice)] = val 361 elif node.__class__ in (ast.Tuple, ast.List): 362 if len(val) == len(node.elts): 363 for telem, tval in zip(node.elts, val): 364 self.node_assign(telem, tval) 365 else: 366 raise ValueError('too many values to unpack')
367
368 - def on_attribute(self, node): # ('value', 'attr', 'ctx')
369 "extract attribute" 370 ctx = node.ctx.__class__ 371 if ctx == ast.Load: 372 sym = self.run(node.value) 373 if hasattr(sym, node.attr): 374 return getattr(sym, node.attr) 375 else: 376 obj = self.run(node.value) 377 fmt = "%s does not have attribute '%s'" 378 msg = fmt % (obj, node.attr) 379 self.raise_exception(node, exc=AttributeError, msg=msg) 380 381 elif ctx == ast.Del: 382 return delattr(sym, node.attr) 383 elif ctx == ast.Store: 384 msg = "attribute for storage: shouldn't be here!" 385 self.raise_exception(node, exc=RuntimeError, msg=msg) 386
387 - def on_assign(self, node): # ('targets', 'value')
388 "simple assignment" 389 val = self.run(node.value) 390 for tnode in node.targets: 391 self.node_assign(tnode, val) 392 return # return val 393
394 - def on_augassign(self, node): # ('target', 'op', 'value')
395 "augmented assign" 396 return self.on_assign(ast.Assign(targets=[node.target], 397 value=ast.BinOp(left = node.target, 398 op = node.op, 399 right= node.value))) 400
401 - def on_slice(self, node): # ():('lower', 'upper', 'step')
402 "simple slice" 403 return slice(self.run(node.lower), 404 self.run(node.upper), 405 self.run(node.step)) 406 407
408 - def on_extslice(self, node): # ():('dims',)
409 "extended slice" 410 return tuple([self.run(tnode) for tnode in node.dims]) 411
412 - def on_subscript(self, node): # ('value', 'slice', 'ctx')
413 "subscript handling -- one of the tricky parts" 414 val = self.run(node.value) 415 nslice = self.run(node.slice) 416 ctx = node.ctx.__class__ 417 if ctx in ( ast.Load, ast.Store): 418 if isinstance(node.slice, (ast.Index, ast.Slice, ast.Ellipsis)): 419 return val.__getitem__(nslice) 420 elif isinstance(node.slice, ast.ExtSlice): 421 return val[(nslice)] 422 else: 423 msg = "subscript with unknown context" 424 self.raise_exception(node, msg=msg) 425
426 - def on_delete(self, node): # ('targets',)
427 "delete statement" 428 for tnode in node.targets: 429 if tnode.ctx.__class__ != ast.Del: 430 break 431 children = [] 432 while tnode.__class__ == ast.Attribute: 433 children.append(tnode.attr) 434 tnode = tnode.value 435 436 if tnode.__class__ == ast.Name: 437 children.append(tnode.id) 438 children.reverse() 439 self.symtable.pop('.'.join(children)) 440 else: 441 msg = "could not delete symbol" 442 self.raise_exception(node, msg=msg) 443
444 - def on_unaryop(self, node): # ('op', 'operand')
445 "unary operator" 446 return op2func(node.op)(self.run(node.operand)) 447
448 - def on_binop(self, node): # ('left', 'op', 'right')
449 "binary operator" 450 return op2func(node.op)(self.run(node.left), 451 self.run(node.right)) 452
453 - def on_boolop(self, node): # ('op', 'values')
454 "boolean operator" 455 val = self.run(node.values[0]) 456 is_and = ast.And == node.op.__class__ 457 if (is_and and val) or (not is_and and not val): 458 for n in node.values: 459 val = op2func(node.op)(val, self.run(n)) 460 if (is_and and not val) or (not is_and and val): 461 break 462 return val 463
464 - def on_compare(self, node): # ('left', 'ops', 'comparators')
465 "comparison operators" 466 lval = self.run(node.left) 467 out = True 468 for op, rnode in zip(node.ops, node.comparators): 469 rval = self.run(rnode) 470 out = op2func(op)(lval, rval) 471 lval = rval 472 if HAS_NUMPY and isinstance(out, numpy.ndarray) and out.any(): 473 break 474 elif not out: 475 break 476 return out 477
478 - def on_print(self, node): # ('dest', 'values', 'nl')
479 """ note: implements Python2 style print statement, not 480 print() function. May need improvement....""" 481 dest = self.run(node.dest) or self.writer 482 end = '' 483 if node.nl: 484 end = '\n' 485 out = [self.run(tnode) for tnode in node.values] 486 if out and len(self.error)==0: 487 self._printer(*out, file=dest, end=end) 488
489 - def _printer(self, *out, **kws):
490 "generic print function" 491 flush = kws.pop('flush', True) 492 fileh = kws.pop('file', self.writer) 493 sep = kws.pop('sep', ' ') 494 end = kws.pop('sep', '\n') 495 496 print(*out, file=fileh, sep=sep, end=end) 497 if flush: 498 fileh.flush()
499
500 - def on_if(self, node): # ('test', 'body', 'orelse')
501 "regular if-then-else statement" 502 block = node.body 503 if not self.run(node.test): 504 block = node.orelse 505 for tnode in block: 506 self.run(tnode) 507
508 - def on_ifexp(self, node): # ('test', 'body', 'orelse')
509 "if expressions" 510 expr = node.orelse 511 if self.run(node.test): 512 expr = node.body 513 return self.run(expr) 514
515 - def on_while(self, node): # ('test', 'body', 'orelse')
516 "while blocks" 517 while self.run(node.test): 518 self._interrupt = None 519 for tnode in node.body: 520 self.run(tnode) 521 if self._interrupt is not None: 522 break 523 if isinstance(self._interrupt, ast.Break): 524 break 525 else: 526 for tnode in node.orelse: 527 self.run(tnode) 528 self._interrupt = None 529
530 - def on_for(self, node): # ('target', 'iter', 'body', 'orelse')
531 "for blocks" 532 for val in self.run(node.iter): 533 self.node_assign(node.target, val) 534 self._interrupt = None 535 for tnode in node.body: 536 self.run(tnode) 537 if self._interrupt is not None: 538 break 539 if isinstance(self._interrupt, ast.Break): 540 break 541 else: 542 for tnode in node.orelse: 543 self.run(tnode) 544 self._interrupt = None 545
546 - def on_listcomp(self, node): # ('elt', 'generators')
547 "list comprehension" 548 out = [] 549 for tnode in node.generators: 550 if tnode.__class__ == ast.comprehension: 551 for val in self.run(tnode.iter): 552 self.node_assign(tnode.target, val) 553 add = True 554 for cond in tnode.ifs: 555 add = add and self.run(cond) 556 if add: 557 out.append(self.run(node.elt)) 558 return out 559
560 - def on_excepthandler(self, node): # ('type', 'name', 'body')
561 "exception handler..." 562 return (self.run(node.type), node.name, node.body) 563
564 - def on_tryexcept(self, node): # ('body', 'handlers', 'orelse')
565 "try/except blocks" 566 no_errors = True 567 for tnode in node.body: 568 self.run(tnode, with_raise=False) 569 no_errors = no_errors and len(self.error) == 0 570 if len(self.error) > 0: 571 e_type, e_value, e_tback = self.error[-1].exc_info 572 for hnd in node.handlers: 573 htype = None 574 if hnd.type is not None: 575 htype = __builtins__.get(hnd.type.id, None) 576 if htype is None or isinstance(e_type(), htype): 577 self.error = [] 578 if hnd.name is not None: 579 self.node_assign(hnd.name, e_value) 580 for tline in hnd.body: 581 self.run(tline) 582 break 583 if no_errors: 584 for tnode in node.orelse: 585 self.run(tnode) 586
587 - def on_raise(self, node): # ('type', 'inst', 'tback')
588 "raise statement: note difference for python 2 and 3" 589 if version_info[0] == 3: 590 excnode = node.exc 591 msgnode = node.cause 592 else: 593 excnode = node.type 594 msgnode = node.inst 595 out = self.run(excnode) 596 msg = ' '.join(out.args) 597 msg2 = self.run(msgnode) 598 if msg2 not in (None, 'None'): 599 msg = "%s: %s" % (msg, msg2) 600 self.raise_exception(None, exc=out.__class__, msg=msg, expr='') 601
602 - def on_call(self, node):
603 "function execution" 604 # ('func', 'args', 'keywords', 'starargs', 'kwargs') 605 func = self.run(node.func) 606 if not hasattr(func, '__call__') and not isinstance(func, type): 607 msg = "'%s' is not callable!!" % (func) 608 self.raise_exception(node, exc=TypeError, msg=msg) 609 610 args = [self.run(targ) for targ in node.args] 611 if node.starargs is not None: 612 args = args + self.run(node.starargs) 613 614 keywords = {} 615 for key in node.keywords: 616 if not isinstance(key, ast.keyword): 617 msg = "keyword error in function call '%s'" % (func) 618 self.raise_exception(node, msg=msg) 619 620 keywords[key.arg] = self.run(key.value) 621 if node.kwargs is not None: 622 keywords.update(self.run(node.kwargs)) 623 624 try: 625 return func(*args, **keywords) 626 except: 627 self.raise_exception(node, exc=RuntimeError, 628 msg = "Error running %s" % (func))
629
630 - def on_arg(self, node): # ('test', 'msg')
631 "arg for function definitions" 632 # print(" ON ARG ! ", node, node.arg) 633 return node.arg 634
635 - def on_functiondef(self, node):
636 "define procedures" 637 # ('name', 'args', 'body', 'decorator_list') 638 if node.decorator_list != []: 639 raise Warning("decorated procedures not supported!") 640 kwargs = [] 641 642 offset = len(node.args.args) - len(node.args.defaults) 643 for idef, defnode in enumerate(node.args.defaults): 644 defval = self.run(defnode) 645 keyval = self.run(node.args.args[idef+offset]) 646 kwargs.append((keyval, defval)) 647 648 if version_info[0] == 3: 649 args = [tnode.arg for tnode in node.args.args[:offset]] 650 else: 651 args = [tnode.id for tnode in node.args.args[:offset]] 652 653 doc = None 654 if (isinstance(node.body[0], ast.Expr) and 655 isinstance(node.body[0].value, ast.Str)): 656 docnode = node.body[0] 657 doc = docnode.value.s 658 659 self.symtable[node.name] = Procedure(node.name, self, doc=doc, 660 body = node.body, 661 lineno = self.lineno, 662 args = args, 663 kwargs = kwargs, 664 vararg = node.args.vararg, 665 varkws = node.args.kwarg)
666
667 -class Procedure(object):
668 """Procedure: user-defined function for asteval 669 670 This stores the parsed ast nodes as from the 671 'functiondef' ast node for later evaluation. 672 """
673 - def __init__(self, name, interp, doc=None, lineno=0, 674 body=None, args=None, kwargs=None, 675 vararg=None, varkws=None):
676 self.name = name 677 self.interpreter = interp 678 self.raise_exc = self.interpreter.raise_exception 679 self.__doc__ = doc 680 self.body = body 681 self.argnames = args 682 self.kwargs = kwargs 683 self.vararg = vararg 684 self.varkws = varkws 685 self.lineno = lineno
686
687 - def __repr__(self):
688 sig = "" 689 if len(self.argnames) > 0: 690 sig = "%s%s" % (sig, ', '.join(self.argnames)) 691 if self.vararg is not None: 692 sig = "%s, *%s" % (sig, self.vararg) 693 if len(self.kwargs) > 0: 694 if len(sig) > 0: 695 sig = "%s, " % sig 696 _kw = ["%s=%s" % (k, v) for k, v in self.kwargs] 697 sig = "%s%s" % (sig, ', '.join(_kw)) 698 699 if self.varkws is not None: 700 sig = "%s, **%s" % (sig, self.varkws) 701 sig = "<Procedure %s(%s)>" % (self.name, sig) 702 if self.__doc__ is not None: 703 sig = "%s\n %s" % (sig, self.__doc__) 704 return sig
705
706 - def __call__(self, *args, **kwargs):
707 symlocals = {} 708 args = list(args) 709 n_args = len(args) 710 n_names = len(self.argnames) 711 n_kws = len(kwargs) 712 713 # may need to move kwargs to args if names align! 714 if (n_args < n_names) and n_kws > 0: 715 for name in self.argnames[n_args:]: 716 if name in kwargs: 717 args.append(kwargs.pop(name)) 718 n_args = len(args) 719 n_names = len(self.argnames) 720 n_kws = len(kwargs) 721 722 if len(self.argnames) > 0 and kwargs is not None: 723 msg = "multiple values for keyword argument '%s' in Procedure %s" 724 for targ in self.argnames: 725 if targ in kwargs: 726 self.raise_exc(None, exc=TypeError, 727 msg=msg % (targ, self.name), 728 lineno=self.lineno) 729 730 if n_args != n_names: 731 msg = None 732 if n_args < n_names: 733 msg = 'not enough arguments for Procedure %s()' % self.name 734 msg = '%s (expected %i, got %i)'% (msg, n_names, n_args) 735 self.raise_exc(None, exc=TypeError, msg=msg) 736 737 738 for argname in self.argnames: 739 symlocals[argname] = args.pop(0) 740 741 try: 742 if self.vararg is not None: 743 symlocals[self.vararg] = tuple(args) 744 745 for key, val in self.kwargs: 746 if key in kwargs: 747 val = kwargs.pop(key) 748 symlocals[key] = val 749 750 if self.varkws is not None: 751 symlocals[self.varkws] = kwargs 752 753 elif len(kwargs) > 0: 754 msg = 'extra keyword arguments for Procedure %s (%s)' 755 msg = msg % (self.name, ','.join(list(kwargs.keys()))) 756 self.raise_exc(None, msg=msg, exc=TypeError, 757 lineno=self.lineno) 758 759 except (ValueError, LookupError, TypeError, 760 NameError, AttributeError): 761 msg = 'incorrect arguments for Procedure %s' % self.name 762 self.raise_exc(None, msg=msg, lineno=self.lineno) 763 764 save_symtable = self.interpreter.symtable.copy() 765 self.interpreter.symtable.update(symlocals) 766 self.interpreter.retval = None 767 retval = None 768 769 # evaluate script of function 770 for node in self.body: 771 self.interpreter.run(node, expr='<>', lineno=self.lineno) 772 if len(self.interpreter.error) > 0: 773 break 774 if self.interpreter.retval is not None: 775 retval = self.interpreter.retval 776 if retval is ReturnedNone: retval = None 777 break 778 779 self.interpreter.symtable = save_symtable 780 symlocals = None 781 return retval
782