Using Asteval

This chapter gives a quick overview of asteval, showing basic usage and the most important features. Further details can be found in the next chapter (Asteval Reference).

Creating and using an asteval Interpreter

The asteval module is very easy to use. Import the module and create an Interpreter:

>>> from asteval import Interpreter
>>> aeval = Interpreter()

and now you have an embedded interpreter for a procedural, mathematical language that is very much like python:

>>> aeval('x = sqrt(3)')
>>> aeval('print(x)')
1.73205080757
>>> aeval('''
for i in range(10):
    print(i, sqrt(i), log(1+1))
''')
0 0.0 0.0
1 1.0 0.69314718056
2 1.41421356237 1.09861228867
3 1.73205080757 1.38629436112
4 2.0 1.60943791243
5 2.2360679775 1.79175946923
6 2.44948974278 1.94591014906
7 2.64575131106 2.07944154168
8 2.82842712475 2.19722457734
9 3.0 2.30258509299

There are lots of options when creating the Interpreter to controller what functionality is and isn’t allowed and to pre-load data and functions. The default interpreter gives a limited but useful version of the Python language.

accessing the symbol table

The symbol table (that is, the mapping between variable and function names and the underlying objects) is a simple dictionary (by default, see Symbol Tables used in asteval for details of an optional alternative) held in the symtable attribute of the interpreter, and can be read or written to:

>>> aeval('x = sqrt(3)')
>>> aeval.symtable['x']
1.73205080757
>>> aeval.symtable['y'] = 100
>>> aeval('print(y/8)')
12.5

Note here the use of true division even though the operands are integers.

As with Python itself, valid symbol names must match the basic regular expression pattern:

valid_name = [a-zA-Z_][a-zA-Z0-9_]*

In addition, certain names are reserved in Python, and cannot be used within the asteval interpreter. These reserved words are:

and, as, assert, async, await, break, class, continue, def, del, elif, else, eval, except, exec, execfile, finally, for, from, global, if, import, in, is, lambda, nonlocal, not, or, pass, print, raise, return, try, while, with, True, False, None, __import__, __package__

built-in functions

At startup, many symbols are loaded into the symbol table from Python’s builtins and the math module. The builtins include several basic Python functions:

abs, all, any, bin, bool, bytearray, bytes, chr, complex, dict, dir, divmod, enumerate, filter, float, format, frozenset, hash, hex, id, int, isinstance, len, list, map, max, min, oct, ord, pow, range, repr, reversed, round, set, slice, sorted, str, sum, tuple, zip

and a large number of named exceptions:

ArithmeticError, AssertionError, AttributeError, BaseException, BufferError, BytesWarning, DeprecationWarning, EOFError, EnvironmentError, Exception, False, FloatingPointError, GeneratorExit, IOError, ImportError, ImportWarning, IndentationError, IndexError, KeyError, KeyboardInterrupt, LookupError, MemoryError, NameError, None, NotImplemented, NotImplementedError, OSError, OverflowError, ReferenceError, RuntimeError, RuntimeWarning, StopIteration, SyntaxError, SyntaxWarning, SystemError, SystemExit, True, TypeError, UnboundLocalError, UnicodeDecodeError, UnicodeEncodeError, UnicodeError, UnicodeTranslateError, UnicodeWarning, ValueError, Warning, ZeroDivisionError

The symbols imported from Python’s math module include:

acos, acosh, asin, asinh, atan, atan2, atanh, ceil, copysign, cos, cosh, degrees, e, exp, fabs, factorial, floor, fmod, frexp, fsum, hypot, isinf, isnan, ldexp, log, log10, log1p, modf, pi, pow, radians, sin, sinh, sqrt, tan, tanh, trunc

If available, about 300 additional symbols are imported from numpy.

conditionals and loops

If-then-else blocks, for-loops (including the optional else block), while loops (also including optional else block), and with blocks are supported, and work exactly as they do in python. Thus:

>>> code = """
sum = 0
for i in range(10):
    sum += i*sqrt(*1.0)
    if i % 4 == 0:
        sum = sum + 1
print("sum = ", sum)
"""
>>> aeval(code)
sum =  114.049534067

comprehensions

list, dict, and set comprehension are supported, acting just as they do in Python. Generators, yield, and async programming are not currently supported.

printing

For printing, asteval emulates Python’s native print() function. You can change where output is sent with the writer argument when creating the interpreter, or suppress printing all together with the no_print option. By default, outputs are sent to sys.stdout.

writing functions

User-defined functions can be written and executed, as in python with a def block, for example:

>>> from asteval import Interpreter
>>> aeval = Interpreter()
>>> code = """def func(a, b, norm=1.0):
...     return (a + b)/norm
... """
>>> aeval(code)
>>> aeval("func(1, 3, norm=10.0)")
0.4

exceptions

Asteval monitors and caches exceptions in the evaluated code. Brief error messages are printed (with Python’s print function, and so using standard output by default), and the full set of exceptions is kept in the error attribute of the Interpreter instance. This error attribute is a list of instances of the asteval ExceptionHolder class, which is accessed through the get_error() method. The error attribute is reset to an empty list at the beginning of each eval(), so that errors are from only the most recent eval().

Thus, to handle and re-raise exceptions from your Python code in a simple REPL loop, you’d want to do something similar to

>>> from asteval import Interpreter
>>> aeval = Interpreter()
>>> while True:
>>>     inp_string = raw_input('dsl:>')
>>>     result = aeval(inp_string)
>>>     if len(aeval.error)>0:
>>>         for err in aeval.error:
>>>             print(err.get_error())
>>>     else:
>>>         print(result)