Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/asteval/astutils.py: 47%
262 statements
« prev ^ index » next coverage.py v7.2.6, created at 2023-05-28 06:19 +0000
« prev ^ index » next coverage.py v7.2.6, created at 2023-05-28 06:19 +0000
1"""
2utility functions for asteval
4 Matthew Newville <newville@cars.uchicago.edu>,
5 The University of Chicago
6"""
7import ast
8import io
9import math
10import numbers
11import re
12from sys import exc_info
13from tokenize import ENCODING as tk_ENCODING
14from tokenize import NAME as tk_NAME
15from tokenize import tokenize as generate_tokens
17builtins = __builtins__
18if not isinstance(builtins, dict):
19 builtins = builtins.__dict__
21HAS_NUMPY = False
22try:
23 import numpy
24 numpy_version = numpy.version.version.split('.', 2)
25 HAS_NUMPY = True
26except ImportError:
27 numpy = None
29MAX_EXPONENT = 10000
30MAX_STR_LEN = 2 << 17 # 256KiB
31MAX_SHIFT = 1000
32MAX_OPEN_BUFFER = 2 << 17
34RESERVED_WORDS = ('and', 'as', 'assert', 'break', 'class', 'continue',
35 'def', 'del', 'elif', 'else', 'except', 'exec',
36 'finally', 'for', 'from', 'global', 'if', 'import',
37 'in', 'is', 'lambda', 'not', 'or', 'pass', 'print',
38 'raise', 'return', 'try', 'while', 'with', 'True',
39 'False', 'None', 'eval', 'execfile', '__import__',
40 '__package__')
42NAME_MATCH = re.compile(r"[a-zA-Z_][a-zA-Z0-9_]*$").match
44UNSAFE_ATTRS = ('__subclasses__', '__bases__', '__globals__', '__code__',
45 '__reduce__', '__reduce_ex__', '__mro__',
46 '__closure__', '__func__', '__self__', '__module__',
47 '__dict__', '__class__', '__call__', '__get__',
48 '__getattribute__', '__subclasshook__', '__new__',
49 '__init__', 'func_globals', 'func_code', 'func_closure',
50 'im_class', 'im_func', 'im_self', 'gi_code', 'gi_frame',
51 'f_locals', '__asteval__')
53# inherit these from python's __builtins__
54FROM_PY = ('ArithmeticError', 'AssertionError', 'AttributeError',
55 'BaseException', 'BufferError', 'BytesWarning',
56 'DeprecationWarning', 'EOFError', 'EnvironmentError',
57 'Exception', 'False', 'FloatingPointError', 'GeneratorExit',
58 'IOError', 'ImportError', 'ImportWarning', 'IndentationError',
59 'IndexError', 'KeyError', 'KeyboardInterrupt', 'LookupError',
60 'MemoryError', 'NameError', 'None',
61 'NotImplementedError', 'OSError', 'OverflowError',
62 'ReferenceError', 'RuntimeError', 'RuntimeWarning',
63 'StopIteration', 'SyntaxError', 'SyntaxWarning', 'SystemError',
64 'SystemExit', 'True', 'TypeError', 'UnboundLocalError',
65 'UnicodeDecodeError', 'UnicodeEncodeError', 'UnicodeError',
66 'UnicodeTranslateError', 'UnicodeWarning', 'ValueError',
67 'Warning', 'ZeroDivisionError', 'abs', 'all', 'any', 'bin',
68 'bool', 'bytearray', 'bytes', 'chr', 'complex', 'dict', 'dir',
69 'divmod', 'enumerate', 'filter', 'float', 'format', 'frozenset',
70 'hash', 'hex', 'id', 'int', 'isinstance', 'len', 'list', 'map',
71 'max', 'min', 'oct', 'ord', 'pow', 'range', 'repr',
72 'reversed', 'round', 'set', 'slice', 'sorted', 'str', 'sum',
73 'tuple', 'zip')
75BUILTINS_TABLE = {sym: builtins[sym] for sym in FROM_PY if sym in builtins}
77# inherit these from python's math
78FROM_MATH = ('acos', 'acosh', 'asin', 'asinh', 'atan', 'atan2', 'atanh',
79 'ceil', 'copysign', 'cos', 'cosh', 'degrees', 'e', 'exp',
80 'fabs', 'factorial', 'floor', 'fmod', 'frexp', 'fsum',
81 'hypot', 'isinf', 'isnan', 'ldexp', 'log', 'log10', 'log1p',
82 'modf', 'pi', 'pow', 'radians', 'sin', 'sinh', 'sqrt', 'tan',
83 'tanh', 'trunc')
85MATH_TABLE = {sym: getattr(math, sym) for sym in FROM_MATH if hasattr(math, sym)}
88FROM_NUMPY = ('Inf', 'NAN', 'abs', 'add', 'alen', 'all', 'amax', 'amin',
89 'angle', 'any', 'append', 'arange', 'arccos', 'arccosh',
90 'arcsin', 'arcsinh', 'arctan', 'arctan2', 'arctanh',
91 'argmax', 'argmin', 'argsort', 'argwhere', 'around', 'array',
92 'array2string', 'asanyarray', 'asarray', 'asarray_chkfinite',
93 'ascontiguousarray', 'asfarray', 'asfortranarray',
94 'asmatrix', 'asscalar', 'atleast_1d', 'atleast_2d',
95 'atleast_3d', 'average', 'bartlett', 'base_repr',
96 'bitwise_and', 'bitwise_not', 'bitwise_or', 'bitwise_xor',
97 'blackman', 'bool', 'broadcast', 'broadcast_arrays', 'byte',
98 'c_', 'cdouble', 'ceil', 'cfloat', 'chararray', 'choose',
99 'clip', 'clongdouble', 'clongfloat', 'column_stack',
100 'common_type', 'complex', 'complex128', 'complex64',
101 'complex_', 'complexfloating', 'compress', 'concatenate',
102 'conjugate', 'convolve', 'copy', 'copysign', 'corrcoef',
103 'correlate', 'cos', 'cosh', 'cov', 'cross', 'csingle',
104 'cumprod', 'cumsum', 'datetime_data', 'deg2rad', 'degrees',
105 'delete', 'diag', 'diag_indices', 'diag_indices_from',
106 'diagflat', 'diagonal', 'diff', 'digitize', 'divide', 'dot',
107 'double', 'dsplit', 'dstack', 'dtype', 'e', 'ediff1d',
108 'empty', 'empty_like', 'equal', 'exp', 'exp2', 'expand_dims',
109 'expm1', 'extract', 'eye', 'fabs', 'fill_diagonal', 'finfo',
110 'fix', 'flatiter', 'flatnonzero', 'fliplr', 'flipud',
111 'float', 'float32', 'float64', 'float_', 'floating', 'floor',
112 'floor_divide', 'fmax', 'fmin', 'fmod', 'format_parser',
113 'frexp', 'frombuffer', 'fromfile', 'fromfunction',
114 'fromiter', 'frompyfunc', 'fromregex', 'fromstring', 'fv',
115 'genfromtxt', 'getbufsize', 'geterr', 'gradient', 'greater',
116 'greater_equal', 'hamming', 'hanning', 'histogram',
117 'histogram2d', 'histogramdd', 'hsplit', 'hstack', 'hypot',
118 'i0', 'identity', 'iinfo', 'imag', 'in1d', 'index_exp',
119 'indices', 'inexact', 'inf', 'info', 'infty', 'inner',
120 'insert', 'int', 'int0', 'int16', 'int32', 'int64', 'int8',
121 'int_', 'int_asbuffer', 'intc', 'integer', 'interp',
122 'intersect1d', 'intp', 'invert', 'ipmt', 'irr', 'iscomplex',
123 'iscomplexobj', 'isfinite', 'isfortran', 'isinf', 'isnan',
124 'isneginf', 'isposinf', 'isreal', 'isrealobj', 'isscalar',
125 'issctype', 'iterable', 'ix_', 'kaiser', 'kron', 'ldexp',
126 'left_shift', 'less', 'less_equal', 'linspace',
127 'little_endian', 'load', 'loads', 'loadtxt', 'log', 'log10',
128 'log1p', 'log2', 'logaddexp', 'logaddexp2', 'logical_and',
129 'logical_not', 'logical_or', 'logical_xor', 'logspace',
130 'long', 'longcomplex', 'longdouble', 'longfloat', 'longlong',
131 'mafromtxt', 'mask_indices', 'mat', 'matrix',
132 'maximum', 'maximum_sctype', 'may_share_memory', 'mean',
133 'median', 'memmap', 'meshgrid', 'mgrid', 'minimum',
134 'mintypecode', 'mirr', 'mod', 'modf', 'msort', 'multiply',
135 'nan', 'nan_to_num', 'nanargmax', 'nanargmin', 'nanmax',
136 'nanmin', 'nansum', 'ndarray', 'ndenumerate', 'ndfromtxt',
137 'ndim', 'ndindex', 'negative', 'newaxis', 'nextafter',
138 'nonzero', 'not_equal', 'nper', 'npv', 'number',
139 'obj2sctype', 'ogrid', 'ones', 'ones_like', 'outer',
140 'packbits', 'percentile', 'pi', 'piecewise', 'place', 'pmt',
141 'poly', 'poly1d', 'polyadd', 'polyder', 'polydiv', 'polyfit',
142 'polyint', 'polymul', 'polysub', 'polyval', 'power', 'ppmt',
143 'prod', 'product', 'ptp', 'put', 'putmask', 'pv', 'r_',
144 'rad2deg', 'radians', 'rank', 'rate', 'ravel', 'real',
145 'real_if_close', 'reciprocal', 'record', 'remainder',
146 'repeat', 'reshape', 'resize', 'restoredot', 'right_shift',
147 'rint', 'roll', 'rollaxis', 'roots', 'rot90', 'round',
148 'round_', 'row_stack', 's_', 'sctype2char', 'searchsorted',
149 'select', 'setbufsize', 'setdiff1d', 'seterr', 'setxor1d',
150 'shape', 'short', 'sign', 'signbit', 'signedinteger', 'sin',
151 'sinc', 'single', 'singlecomplex', 'sinh', 'size',
152 'sometrue', 'sort', 'sort_complex', 'spacing', 'split',
153 'sqrt', 'square', 'squeeze', 'std', 'str', 'str_',
154 'subtract', 'sum', 'swapaxes', 'take', 'tan', 'tanh',
155 'tensordot', 'tile', 'trace', 'transpose', 'trapz', 'tri',
156 'tril', 'tril_indices', 'tril_indices_from', 'trim_zeros',
157 'triu', 'triu_indices', 'triu_indices_from', 'true_divide',
158 'trunc', 'ubyte', 'uint', 'uint0', 'uint16', 'uint32',
159 'uint64', 'uint8', 'uintc', 'uintp', 'ulonglong', 'union1d',
160 'unique', 'unravel_index', 'unsignedinteger', 'unwrap',
161 'ushort', 'vander', 'var', 'vdot', 'vectorize', 'vsplit',
162 'vstack', 'where', 'who', 'zeros', 'zeros_like',
163 'fft', 'linalg', 'polynomial', 'random')
165NUMPY_RENAMES = {'ln': 'log', 'asin': 'arcsin', 'acos': 'arccos',
166 'atan': 'arctan', 'atan2': 'arctan2', 'atanh':
167 'arctanh', 'acosh': 'arccosh', 'asinh': 'arcsinh'}
169if HAS_NUMPY:
170 numpy_deprecated = []
171 if int(numpy_version[0]) == 1 and int(numpy_version[1]) >= 20:
172 # aliases deprecated in NumPy v1.20.0
173 numpy_deprecated = ['str', 'bool', 'int', 'float', 'complex', 'pv', 'rate',
174 'pmt', 'ppmt', 'npv', 'nper', 'long', 'mirr', 'fv',
175 'irr', 'ipmt']
176 if int(numpy_version[0]) == 1 and int(numpy_version[1]) >= 24:
177 # aliases deprecated in NumPy v1.24.0
178 numpy_deprecated += ['int0', 'uint0', 'bool8']
180 FROM_NUMPY = tuple(set(FROM_NUMPY) - set(numpy_deprecated))
182 FROM_NUMPY = tuple(sym for sym in FROM_NUMPY if hasattr(numpy, sym))
183 NUMPY_RENAMES = {sym: value for sym, value in NUMPY_RENAMES.items() if hasattr(numpy, value)}
185 NUMPY_TABLE = {}
186 for sym in FROM_NUMPY:
187 NUMPY_TABLE[sym] = getattr(numpy, sym)
188 for sname, sym in NUMPY_RENAMES.items():
189 NUMPY_TABLE[sname] = getattr(numpy, sym)
190else:
191 NUMPY_TABLE = {}
194def _open(filename, mode='r', buffering=-1, encoding=None):
195 """read only version of open()"""
196 if mode not in ('r', 'rb', 'rU'):
197 raise RuntimeError("Invalid open file mode, must be 'r', 'rb', or 'rU'")
198 if buffering > MAX_OPEN_BUFFER:
199 raise RuntimeError(f"Invalid buffering value, max buffer size is {MAX_OPEN_BUFFER}")
200 return open(filename, mode, buffering, encoding=encoding)
203def _type(obj):
204 """type that prevents varargs and varkws"""
205 return type(obj).__name__
208LOCALFUNCS = {'open': _open, 'type': _type}
211# Safe versions of functions to prevent denial of service issues
213def safe_pow(base, exp):
214 """safe version of pow"""
215 if isinstance(exp, numbers.Number):
216 if exp > MAX_EXPONENT:
217 raise RuntimeError(f"Invalid exponent, max exponent is {MAX_EXPONENT}")
218 elif HAS_NUMPY and isinstance(exp, numpy.ndarray):
219 if numpy.nanmax(exp) > MAX_EXPONENT:
220 raise RuntimeError(f"Invalid exponent, max exponent is {MAX_EXPONENT}")
221 return base ** exp
224def safe_mult(arg1, arg2):
225 """safe version of multiply"""
226 if isinstance(arg1, str) and isinstance(arg2, int) and len(arg1) * arg2 > MAX_STR_LEN:
227 raise RuntimeError(f"String length exceeded, max string length is {MAX_STR_LEN}")
228 return arg1 * arg2
231def safe_add(arg1, arg2):
232 """safe version of add"""
233 if isinstance(arg1, str) and isinstance(arg2, str) and len(arg1) + len(arg2) > MAX_STR_LEN:
234 raise RuntimeError(f"String length exceeded, max string length is {MAX_STR_LEN}")
235 return arg1 + arg2
238def safe_lshift(arg1, arg2):
239 """safe version of lshift"""
240 if isinstance(arg2, numbers.Number):
241 if arg2 > MAX_SHIFT:
242 raise RuntimeError(f"Invalid left shift, max left shift is {MAX_SHIFT}")
243 elif HAS_NUMPY and isinstance(arg2, numpy.ndarray):
244 if numpy.nanmax(arg2) > MAX_SHIFT:
245 raise RuntimeError(f"Invalid left shift, max left shift is {MAX_SHIFT}")
246 return arg1 << arg2
249OPERATORS = {ast.Is: lambda a, b: a is b,
250 ast.IsNot: lambda a, b: a is not b,
251 ast.In: lambda a, b: a in b,
252 ast.NotIn: lambda a, b: a not in b,
253 ast.Add: safe_add,
254 ast.BitAnd: lambda a, b: a & b,
255 ast.BitOr: lambda a, b: a | b,
256 ast.BitXor: lambda a, b: a ^ b,
257 ast.Div: lambda a, b: a / b,
258 ast.FloorDiv: lambda a, b: a // b,
259 ast.LShift: safe_lshift,
260 ast.RShift: lambda a, b: a >> b,
261 ast.Mult: safe_mult,
262 ast.Pow: safe_pow,
263 ast.MatMult: lambda a, b: a @ b,
264 ast.Sub: lambda a, b: a - b,
265 ast.Mod: lambda a, b: a % b,
266 ast.And: lambda a, b: a and b,
267 ast.Or: lambda a, b: a or b,
268 ast.Eq: lambda a, b: a == b,
269 ast.Gt: lambda a, b: a > b,
270 ast.GtE: lambda a, b: a >= b,
271 ast.Lt: lambda a, b: a < b,
272 ast.LtE: lambda a, b: a <= b,
273 ast.NotEq: lambda a, b: a != b,
274 ast.Invert: lambda a: ~a,
275 ast.Not: lambda a: not a,
276 ast.UAdd: lambda a: +a,
277 ast.USub: lambda a: -a}
280def valid_symbol_name(name):
281 """Determine whether the input symbol name is a valid name.
283 Arguments
284 ---------
285 name : str
286 name to check for validity.
288 Returns
289 --------
290 valid : bool
291 whether name is a a valid symbol name
293 This checks for Python reserved words and that the name matches
294 the regular expression ``[a-zA-Z_][a-zA-Z0-9_]``
295 """
296 if name in RESERVED_WORDS:
297 return False
299 gen = generate_tokens(io.BytesIO(name.encode('utf-8')).readline)
300 typ, _, start, end, _ = next(gen)
301 if typ == tk_ENCODING:
302 typ, _, start, end, _ = next(gen)
303 return typ == tk_NAME and start == (1, 0) and end == (1, len(name))
306def op2func(oper):
307 """Return function for operator nodes."""
308 return OPERATORS[oper.__class__]
311class Empty:
312 """Empty class."""
313 def __init__(self):
314 """TODO: docstring in public method."""
315 pass
317 def __nonzero__(self):
318 """Empty is TODO: docstring in magic method."""
319 return False
322ReturnedNone = Empty()
325class ExceptionHolder:
326 """Basic exception handler."""
328 def __init__(self, node, exc=None, msg='', expr=None, lineno=None):
329 """TODO: docstring in public method."""
330 self.node = node
331 self.expr = expr
332 self.msg = msg
333 self.exc = exc
334 self.lineno = lineno
335 self.exc_info = exc_info()
336 if self.exc is None and self.exc_info[0] is not None:
337 self.exc = self.exc_info[0]
338 if self.msg == '' and self.exc_info[1] is not None:
339 self.msg = self.exc_info[1]
341 def get_error(self):
342 """Retrieve error data."""
343 col_offset = -1
344 if self.node is not None:
345 try:
346 col_offset = self.node.col_offset
347 except AttributeError:
348 pass
349 try:
350 exc_name = self.exc.__name__
351 except AttributeError:
352 exc_name = str(self.exc)
353 if exc_name in (None, 'None'):
354 exc_name = 'UnknownError'
356 out = [f" {self.expr}"]
357 if col_offset > 0:
358 out.append(f" {col_offset*' '}^^^")
359 out.append(str(self.msg))
360 return (exc_name, '\n'.join(out))
363class NameFinder(ast.NodeVisitor):
364 """Find all symbol names used by a parsed node."""
366 def __init__(self):
367 """TODO: docstring in public method."""
368 self.names = []
369 ast.NodeVisitor.__init__(self)
371 def generic_visit(self, node):
372 """TODO: docstring in public method."""
373 if node.__class__.__name__ == 'Name':
374 if node.ctx.__class__ == ast.Load and node.id not in self.names:
375 self.names.append(node.id)
376 ast.NodeVisitor.generic_visit(self, node)
379def get_ast_names(astnode):
380 """Return symbol Names from an AST node."""
381 finder = NameFinder()
382 finder.generic_visit(astnode)
383 return finder.names
386def make_symbol_table(use_numpy=True, **kws):
387 """Create a default symboltable, taking dict of user-defined symbols.
389 Arguments
390 ---------
391 numpy : bool, optional
392 whether to include symbols from numpy
393 kws : optional
394 additional symbol name, value pairs to include in symbol table
396 Returns
397 --------
398 symbol_table : dict
399 a symbol table that can be used in `asteval.Interpereter`
401 """
402 symtable = {}
404 symtable.update(BUILTINS_TABLE)
405 symtable.update(MATH_TABLE)
406 if use_numpy:
407 symtable.update(NUMPY_TABLE)
408 symtable.update(LOCALFUNCS)
409 symtable.update(kws)
411 return symtable
415class Procedure:
416 """Procedure: user-defined function for asteval.
418 This stores the parsed ast nodes as from the 'functiondef' ast node
419 for later evaluation.
421 """
423 def __init__(self, name, interp, doc=None, lineno=0,
424 body=None, args=None, kwargs=None,
425 vararg=None, varkws=None):
426 """TODO: docstring in public method."""
427 self.__ininit__ = True
428 self.name = name
429 self.__name__ = self.name
430 self.__asteval__ = interp
431 self.raise_exc = self.__asteval__.raise_exception
432 self.__doc__ = doc
433 self.body = body
434 self.argnames = args
435 self.kwargs = kwargs
436 self.vararg = vararg
437 self.varkws = varkws
438 self.lineno = lineno
439 self.__ininit__ = False
441 def __setattr__(self, attr, val):
442 if not getattr(self, '__ininit__', True):
443 self.raise_exc(None, exc=TypeError,
444 msg="procedure is read-only")
445 self.__dict__[attr] = val
447 def __dir__(self):
448 return ['name']
450 def __repr__(self):
451 """TODO: docstring in magic method."""
452 sig = ""
453 if len(self.argnames) > 0:
454 sig = sig + ', '.join(self.argnames)
455 if self.vararg is not None:
456 sig = sig + f"*{self.vararg}"
457 if len(self.kwargs) > 0:
458 if len(sig) > 0:
459 sig = f"{sig}, "
460 _kw = [f"{k}={v}" for k, v in self.kwargs]
461 sig = f"{sig}{', '.join(_kw)}"
463 if self.varkws is not None:
464 sig = f"%sig, **{self.varkws}"
465 sig = f"<Procedure {self.name}({sig})>"
466 if self.__doc__ is not None:
467 sig = f"{sig}\n {self.__doc__}"
468 return sig
470 def __call__(self, *args, **kwargs):
471 """TODO: docstring in public method."""
472 symlocals = {}
473 args = list(args)
474 nargs = len(args)
475 nkws = len(kwargs)
476 nargs_expected = len(self.argnames)
477 # check for too few arguments, but the correct keyword given
478 if (nargs < nargs_expected) and nkws > 0:
479 for name in self.argnames[nargs:]:
480 if name in kwargs:
481 args.append(kwargs.pop(name))
482 nargs = len(args)
483 nargs_expected = len(self.argnames)
484 nkws = len(kwargs)
485 if nargs < nargs_expected:
486 msg = f"{self.name}() takes at least"
487 msg = f"{msg} {nargs_expected} arguments, got {nargs}"
488 self.raise_exc(None, exc=TypeError, msg=msg)
489 # check for multiple values for named argument
490 if len(self.argnames) > 0 and kwargs is not None:
491 msg = "multiple values for keyword argument"
492 for targ in self.argnames:
493 if targ in kwargs:
494 msg = f"{msg} '{targ}' in Procedure {self.name}"
495 self.raise_exc(None, exc=TypeError, msg=msg, lineno=self.lineno)
497 # check more args given than expected, varargs not given
498 if nargs != nargs_expected:
499 msg = None
500 if nargs < nargs_expected:
501 msg = f"not enough arguments for Procedure {self.name}()"
502 msg = f"{msg} (expected {nargs_expected}, got {nargs}"
503 self.raise_exc(None, exc=TypeError, msg=msg)
505 if nargs > nargs_expected and self.vararg is None:
506 if nargs - nargs_expected > len(self.kwargs):
507 msg = f"too many arguments for {self.name}() expected at most"
508 msg = f"{msg} {len(self.kwargs)+nargs_expected}, got {nargs}"
509 self.raise_exc(None, exc=TypeError, msg=msg)
511 for i, xarg in enumerate(args[nargs_expected:]):
512 kw_name = self.kwargs[i][0]
513 if kw_name not in kwargs:
514 kwargs[kw_name] = xarg
516 for argname in self.argnames:
517 symlocals[argname] = args.pop(0)
519 try:
520 if self.vararg is not None:
521 symlocals[self.vararg] = tuple(args)
523 for key, val in self.kwargs:
524 if key in kwargs:
525 val = kwargs.pop(key)
526 symlocals[key] = val
528 if self.varkws is not None:
529 symlocals[self.varkws] = kwargs
531 elif len(kwargs) > 0:
532 msg = f"extra keyword arguments for Procedure {self.name}: "
533 msg = msg + ','.join(list(kwargs.keys()))
534 self.raise_exc(None, msg=msg, exc=TypeError,
535 lineno=self.lineno)
537 except (ValueError, LookupError, TypeError,
538 NameError, AttributeError):
539 msg = f"incorrect arguments for Procedure {self.name}"
540 self.raise_exc(None, msg=msg, lineno=self.lineno)
542 save_symtable = self.__asteval__.symtable.copy()
543 self.__asteval__.symtable.update(symlocals)
544 self.__asteval__.retval = None
545 self.__asteval__._calldepth += 1
546 retval = None
548 # evaluate script of function
549 for node in self.body:
550 self.__asteval__.run(node, expr='<>', lineno=self.lineno)
551 if len(self.__asteval__.error) > 0:
552 break
553 if self.__asteval__.retval is not None:
554 retval = self.__asteval__.retval
555 self.__asteval__.retval = None
556 if retval is ReturnedNone:
557 retval = None
558 break
560 self.__asteval__.symtable = save_symtable
561 self.__asteval__._calldepth -= 1
562 symlocals = None
563 return retval