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

1""" 

2utility functions for asteval 

3 

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 

16 

17builtins = __builtins__ 

18if not isinstance(builtins, dict): 

19 builtins = builtins.__dict__ 

20 

21HAS_NUMPY = False 

22try: 

23 import numpy 

24 numpy_version = numpy.version.version.split('.', 2) 

25 HAS_NUMPY = True 

26except ImportError: 

27 numpy = None 

28 

29MAX_EXPONENT = 10000 

30MAX_STR_LEN = 2 << 17 # 256KiB 

31MAX_SHIFT = 1000 

32MAX_OPEN_BUFFER = 2 << 17 

33 

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__') 

41 

42NAME_MATCH = re.compile(r"[a-zA-Z_][a-zA-Z0-9_]*$").match 

43 

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__') 

52 

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') 

74 

75BUILTINS_TABLE = {sym: builtins[sym] for sym in FROM_PY if sym in builtins} 

76 

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') 

84 

85MATH_TABLE = {sym: getattr(math, sym) for sym in FROM_MATH if hasattr(math, sym)} 

86 

87 

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') 

164 

165NUMPY_RENAMES = {'ln': 'log', 'asin': 'arcsin', 'acos': 'arccos', 

166 'atan': 'arctan', 'atan2': 'arctan2', 'atanh': 

167 'arctanh', 'acosh': 'arccosh', 'asinh': 'arcsinh'} 

168 

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'] 

179 

180 FROM_NUMPY = tuple(set(FROM_NUMPY) - set(numpy_deprecated)) 

181 

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)} 

184 

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 = {} 

192 

193 

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) 

201 

202 

203def _type(obj): 

204 """type that prevents varargs and varkws""" 

205 return type(obj).__name__ 

206 

207 

208LOCALFUNCS = {'open': _open, 'type': _type} 

209 

210 

211# Safe versions of functions to prevent denial of service issues 

212 

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 

222 

223 

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 

229 

230 

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 

236 

237 

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 

247 

248 

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} 

278 

279 

280def valid_symbol_name(name): 

281 """Determine whether the input symbol name is a valid name. 

282 

283 Arguments 

284 --------- 

285 name : str 

286 name to check for validity. 

287 

288 Returns 

289 -------- 

290 valid : bool 

291 whether name is a a valid symbol name 

292 

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 

298 

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)) 

304 

305 

306def op2func(oper): 

307 """Return function for operator nodes.""" 

308 return OPERATORS[oper.__class__] 

309 

310 

311class Empty: 

312 """Empty class.""" 

313 def __init__(self): 

314 """TODO: docstring in public method.""" 

315 pass 

316 

317 def __nonzero__(self): 

318 """Empty is TODO: docstring in magic method.""" 

319 return False 

320 

321 

322ReturnedNone = Empty() 

323 

324 

325class ExceptionHolder: 

326 """Basic exception handler.""" 

327 

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] 

340 

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' 

355 

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)) 

361 

362 

363class NameFinder(ast.NodeVisitor): 

364 """Find all symbol names used by a parsed node.""" 

365 

366 def __init__(self): 

367 """TODO: docstring in public method.""" 

368 self.names = [] 

369 ast.NodeVisitor.__init__(self) 

370 

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) 

377 

378 

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 

384 

385 

386def make_symbol_table(use_numpy=True, **kws): 

387 """Create a default symboltable, taking dict of user-defined symbols. 

388 

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 

395 

396 Returns 

397 -------- 

398 symbol_table : dict 

399 a symbol table that can be used in `asteval.Interpereter` 

400 

401 """ 

402 symtable = {} 

403 

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) 

410 

411 return symtable 

412 

413 

414 

415class Procedure: 

416 """Procedure: user-defined function for asteval. 

417 

418 This stores the parsed ast nodes as from the 'functiondef' ast node 

419 for later evaluation. 

420 

421 """ 

422 

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 

440 

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 

446 

447 def __dir__(self): 

448 return ['name'] 

449 

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)}" 

462 

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 

469 

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) 

496 

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) 

504 

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) 

510 

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 

515 

516 for argname in self.argnames: 

517 symlocals[argname] = args.pop(0) 

518 

519 try: 

520 if self.vararg is not None: 

521 symlocals[self.vararg] = tuple(args) 

522 

523 for key, val in self.kwargs: 

524 if key in kwargs: 

525 val = kwargs.pop(key) 

526 symlocals[key] = val 

527 

528 if self.varkws is not None: 

529 symlocals[self.varkws] = kwargs 

530 

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) 

536 

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) 

541 

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 

547 

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 

559 

560 self.__asteval__.symtable = save_symtable 

561 self.__asteval__._calldepth -= 1 

562 symlocals = None 

563 return retval