From 70eb46be0c7429d998766766f8ccc89d7ef29094 Mon Sep 17 00:00:00 2001 From: Matthew Newville Date: Sun, 19 Jan 2025 14:11:57 -0600 Subject: [PATCH] using ast functions to construct better error messages --- asteval/asteval.py | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/asteval/asteval.py b/asteval/asteval.py index e059f49..0b64ab9 100644 --- a/asteval/asteval.py +++ b/asteval/asteval.py @@ -150,6 +150,7 @@ def __init__(self, symtable=None, nested_symtable=False, self.retval = None self._calldepth = 0 self.lineno = 0 + self.code_text = [] self.start_time = time.time() self.node_handlers = {} @@ -221,8 +222,13 @@ def raise_exception(self, node, exc=None, msg='', expr=None, lineno=None): """Add an exception.""" if expr is not None: self.expr = expr + msg = str(msg) - err = ExceptionHolder(node, exc=exc, msg=msg, expr=self.expr, lineno=lineno) + text = self.expr + if len(self.code_text) > 0: + text = self.code_text[-1] + err = ExceptionHolder(node, exc=exc, msg=msg, expr=self.expr, + text=text, lineno=lineno) self._interrupt = ast.Raise() self.error.append(err) @@ -238,6 +244,7 @@ def raise_exception(self, node, exc=None, msg='', expr=None, lineno=None): while exc is None and len(self.error) > 0: err = self.error.pop() exc = err.exc + if exc is None: exc = Exception if len(err.msg) == 0 and len(self.error_msg) == 0 and len(self.error) > 1: @@ -264,7 +271,7 @@ def parse(self, text): self.raise_exception(None, exc=SyntaxError, expr=text) except: self.raise_exception(None, exc=RuntimeError, expr=text) - + out = ast.fix_missing_locations(out) return out def run(self, node, expr=None, lineno=None, with_raise=True): @@ -273,7 +280,6 @@ def run(self, node, expr=None, lineno=None, with_raise=True): # run(None) and expect a None in return. if isinstance(node, str): return self.eval(node, raise_errors=with_raise) - out = None if len(self.error) > 0: return out @@ -288,6 +294,7 @@ def run(self, node, expr=None, lineno=None, with_raise=True): self.lineno = lineno if expr is not None: self.expr = expr + self.code_text.append(expr) # get handler for this node: # on_xxx with handle nodes of type 'xxx', etc @@ -296,6 +303,7 @@ def run(self, node, expr=None, lineno=None, with_raise=True): except KeyError: self.raise_exception(None, exc=NotImplementedError, expr=self.expr) + # run the handler: this will likely generate # recursive calls into this run method. try: @@ -307,6 +315,7 @@ def run(self, node, expr=None, lineno=None, with_raise=True): if with_raise and self.expr is not None: self.raise_exception(node, expr=self.expr) + # avoid too many repeated error messages (yes, this needs to be "2") if len(self.error) > 2: self._remove_duplicate_errors() @@ -573,7 +582,7 @@ def on_attribute(self, node): # ('value', 'attr', 'ctx') sym = self.run(node.value) if ctx == ast.Del: return delattr(sym, node.attr) - + return safe_getattr(sym, node.attr, self.raise_exception, node) @@ -958,6 +967,7 @@ def on_functiondef(self, node): self.symtable[node.name] = Procedure(node.name, self, doc=doc, lineno=self.lineno, body=node.body, + text=ast.unparse(node), args=args, kwargs=kwargs, vararg=vararg, varkws=varkws) if node.name in self.no_deepcopy: