Skip to content

Commit 8e0700e

Browse files
committed
include text and better lineno info for better exceptions
1 parent 6970d89 commit 8e0700e

File tree

1 file changed

+28
-14
lines changed

1 file changed

+28
-14
lines changed

asteval/astutils.py

Lines changed: 28 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -310,10 +310,10 @@ def get_field(self, field_name, args, kwargs):
310310
else:
311311
obj = obj[i]
312312
return obj, first
313-
313+
314314
def safe_format(_string, raise_exc, node, *args, **kwargs):
315315
formatter = SafeFormatter(raise_exc, node)
316-
return formatter.vformat(_string, args, kwargs)
316+
return formatter.vformat(_string, args, kwargs)
317317

318318
def valid_symbol_name(name):
319319
"""Determine whether the input symbol name is a valid name.
@@ -364,13 +364,24 @@ def __repr__(self):
364364

365365
class ExceptionHolder:
366366
"""Basic exception handler."""
367-
def __init__(self, node, exc=None, msg='', expr=None, lineno=None):
367+
def __init__(self, node, exc=None, msg='', expr=None,
368+
text=None, lineno=None):
368369
"""TODO: docstring in public method."""
369370
self.node = node
370371
self.expr = expr
371372
self.msg = msg
372373
self.exc = exc
374+
self.text = text
373375
self.lineno = lineno
376+
self.end_lineno = lineno
377+
self.col_offset = 0
378+
if lineno is None:
379+
try:
380+
self.lineno = node.lineno
381+
self.end_lineno = node.end_lineno
382+
self.col_offset = node.col_offset
383+
except:
384+
pass
374385
self.exc_info = exc_info()
375386
if self.exc is None and self.exc_info[0] is not None:
376387
self.exc = self.exc_info[0]
@@ -379,22 +390,22 @@ def __init__(self, node, exc=None, msg='', expr=None, lineno=None):
379390

380391
def get_error(self):
381392
"""Retrieve error data."""
382-
col_offset = -1
383-
if self.node is not None:
384-
try:
385-
col_offset = self.node.col_offset
386-
except AttributeError:
387-
pass
388393
try:
389394
exc_name = self.exc.__name__
390395
except AttributeError:
391396
exc_name = str(self.exc)
392397
if exc_name in (None, 'None'):
393398
exc_name = 'UnknownError'
394399

395-
out = [f" {self.expr}"]
396-
if col_offset > 0:
397-
out.append(f" {col_offset*' '}^^^^")
400+
out = []
401+
try:
402+
lines = self.text.split('\n')
403+
line = '\n'.join(lines[self.lineno-1:self.end_lineno])
404+
out.append(f"{line}")
405+
except:
406+
out.append(f"{self.expr}")
407+
if self.col_offset > 0:
408+
out.append(f"{self.col_offset*' '}^^^^")
398409
out.append(f"{exc_name}: {self.msg}")
399410
return (exc_name, '\n'.join(out))
400411

@@ -546,7 +557,7 @@ class Procedure:
546557
547558
"""
548559

549-
def __init__(self, name, interp, doc=None, lineno=0,
560+
def __init__(self, name, interp, doc=None, lineno=None,
550561
body=None, args=None, kwargs=None,
551562
vararg=None, varkws=None):
552563
"""TODO: docstring in public method."""
@@ -562,6 +573,7 @@ def __init__(self, name, interp, doc=None, lineno=0,
562573
self.__vararg__ = vararg
563574
self.__varkws__ = varkws
564575
self.lineno = lineno
576+
self.__text__ = ast.unparse(self.__body__)
565577
self.__ininit__ = False
566578

567579
def __setattr__(self, attr, val):
@@ -703,8 +715,9 @@ def __call__(self, *args, **kwargs):
703715
retval = None
704716

705717
# evaluate script of function
718+
self.__asteval__.code_text.append(self.__text__)
706719
for node in self.__body__:
707-
self.__asteval__.run(node, expr='<>', lineno=self.lineno)
720+
self.__asteval__.run(node, lineno=node.lineno)
708721
if len(self.__asteval__.error) > 0:
709722
break
710723
if self.__asteval__.retval is not None:
@@ -715,6 +728,7 @@ def __call__(self, *args, **kwargs):
715728
break
716729

717730
self.__asteval__.symtable = save_symtable
731+
self.__asteval__.code_text.pop()
718732
self.__asteval__._calldepth -= 1
719733
symlocals = None
720734
return retval

0 commit comments

Comments
 (0)