asteval

repository·master·Indexed 42 days ago

https://github.com/lmfit/asteval

A safe, minimalistic evaluator of Python expressions and statements using the AST module. It provides a restricted interpreter suitable for embedded macro languages or mathematical calculators, featuring a managed symbol table and configurable support for Python syntax such as loops, conditionals, and function definitions.

Tokens
8K
Snippets
23
Records
36
Agent score
50%

What's inside asteval

  1. What is ASTEVAL?

    master

    ASTEVAL is a minimal Python AST (Abstract Syntax Tree) evaluator designed to evaluate Python expressions and statements more safely than Python's built-in eval() and more capably than ast.literal_eval().

    It provides an embedded interpreter that evaluates a subset of the Python language, with a primary focus on mathematical expressions. It includes functions from the math module by default and automatically includes a large number of numpy functions if numpy is installed. It also supports numpy-financial functions if that package is present.

    Key features supported by default include:

    • Mathematical expressions
    • Array slicing and subscripting
    • Control flow: if-then-else conditionals, while loops, for loops, and try-except blocks
    • List comprehensions
    • User-defined functions
    • Standard Python data structures: strings, dictionaries, tuples, lists, sets, and numpy arrays (including their built-in methods).
  2. What is Asteval and when to use it

    master

    Asteval is a restricted Python interpreter designed to safely evaluate Python expressions and statements using Python's ast module. It is intended for use cases where you need to handle user input more safely than Python's built-in eval(), such as:

    • Embedded macro languages: Providing a scripting interface within a larger GUI or Web application.
    • Mathematical calculators: Performing complex mathematical calculations with built-in support for math and NumPy functions.

    It works by converting input into an Abstract Syntax Tree (AST) and walking through it, ensuring parsing is identical to standard Python while enforcing safety boundaries.

  3. What is asteval and when should I use it?

    master

    The asteval module provides a restricted version of Python's built-in eval() function. It allows you to evaluate a large subset of the Python language within a Python program while providing a safer environment for executing user input or embedding a scientific calculator into a larger application.

    Key Characteristics:

    • Safety: Unlike eval(), asteval forbids several actions by default. It does not support the import statement (unless explicitly enabled), preventing user code from accessing sensitive modules like os or sys unless they are explicitly provided in the symbol table.
    • Functionality: It is designed as a procedural, mathematically-oriented domain-specific language. It supports complex Python constructs like slicing, complex numbers, keyword arguments, conditionals, loops, exception handling, and user-defined functions.
    • Implementation: It uses Python's ast (Abstract Syntax Tree) module to ensure that valid Python expressions are parsed correctly according to Python's own grammar, making it more robust than manual parsing implementations.

    Performance Considerations:

    asteval is generally slower than native Python execution (often around 4x slower) because evaluating the AST involves many function calls. However, for certain scientific use cases involving numpy, its performance can approach that of eval or the numexpr module.

  4. Use nested symbol tables with `nested_symtable=True`

    master

    Starting with version 0.9.31, you can enable a more advanced, nested symbol table by passing nested_symtable=True to the Interpreter constructor.

    Key Features:

    • Attribute Access: The symbol table uses a Group object (a dict subclass) that allows accessing symbols via dot notation (e.g., aeval.symtable.x) in addition to standard dictionary keys.
    • Subgroups and Automated Searching: The table can contain subgroups. The interpreter uses a special attribute _searchgroups (a tuple of group names) to determine where to look for symbols if they aren't found in the top-level group.
    • Behavior: When looking up a symbol, the interpreter searches the top-level group first, then iterates through the groups listed in _searchgroups. This allows for a behavior similar to from math import *, where functions like cos or pi can be found in a math subgroup without needing to prefix them (e.g., cos(x) works even if cos is inside the math group).

    By default, mathematical functions from math and numpy are placed in a subgroup named math.

    from asteval import Interpreter
    
    aeval = Interpreter(nested_symtable=True)
    aeval('x = 3')
    
    # Access via dictionary key
    print(aeval.symtable['x'])
    
    # Access via attribute syntax
    print(aeval.symtable.x)
    
    # Assign via attribute syntax
    aeval.symtable.y = 7
    print(aeval('print(x + y)'))
  5. Understand the safety model of asteval

    master

    Asteval is designed as a safer alternative to Python's built-in eval() function, but it cannot guarantee absolute safety from malicious code. It works by blacklisting dangerous attributes, prohibiting certain Python statements, and implementing runtime checks to prevent common exploits and resource exhaustion.

    Key Safety Features:

    • Attribute Blacklisting: Prevents access to dunder (double underscore) attributes (e.g., __globals__, __class__) and specific internal attributes like func_globals or __mro__ to prevent OS access or interpreter crashes.
    • Statement Restrictions: By default, import and __import__ are disabled. Creating classes or modules is prohibited.
    • Runtime Limits: To prevent Denial of Service (DoS) via resource hogging, asteval enforces limits on:
      • Exponent values (x**p) where p > 10,000.
      • Shift operations (x << p) where p > 1000.
      • Statement length (> 50,000 bytes).
      • String operation lengths (> 262,144 bytes).
      • Number of open buffers (> 262,144).
      • File opening modes (restricted to 'r', 'rb', or 'ru').
    • Recent Security Hardening (v1.0.6 - v1.0.10): Removed string.format, restricted numpy submodules (linalg, fft, polynomial), and disabled numpy ndarray methods like tofile, dump, and the ctypes attribute.
  6. Limitations and restrictions in the ASTEVAL language

    master

    To maintain safety and focus on mathematical calculations, ASTEVAL intentionally restricts several Python features. When using ASTEVAL as an embedded macro language, be aware of the following limitations:

    • Symbol Management: All variable and function names are held in a single symbol table (a flat namespace dictionary by default).
    • No Class Creation: Creating new classes is not allowed.
    • No Module Imports: Importing modules is disallowed unless specifically enabled.
    • Unsupported Syntax: Decorators, generators, type hints, yield, await, and async programming are not supported.
    • Restricted Built-ins: Many built-in functions are disallowed, specifically eval, getattr, hasattr, setattr, and delattr.
    • Attribute Access: Accessing object attributes that could provide access to the underlying Python interpreter is restricted.
  7. Understand and manipulate Symbol Tables in asteval

    master

    The symbol table is the central repository for all data used by the Interpreter. It maps variable names to their values. When you execute an expression, the Interpreter looks up names (like variables or functions) in the symbol table and sets new values in it.

    By default, the symbol table is a flat dictionary stored in the Interpreter.symtable attribute. You can directly manipulate this dictionary from your Python code to inject, replace, or remove symbols available to the interpreter.

    Constraints:

    • Symbol names must be valid Python identifiers ([a-zA-Z_][a-zA-Z0-9_]*).
    • Symbol names cannot be Python reserved words.
    from asteval import Interpreter
    
    aeval = Interpreter()
    # Inject a symbol directly into the symbol table
    aeval.symtable['x'] = 10
    # The interpreter now recognizes 'x'
    print(aeval('sqrt(x)'))
  8. Control flow and comprehensions in asteval

    master

    asteval supports standard Python control flow structures, including:

    • if-then-else blocks
    • for loops (including else blocks)
    • while loops (including else blocks)
    • with blocks
    • List, dict, and set comprehensions

    Unsupported features: Generators, yield, and async programming are not currently supported.

    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)
  9. Understand Asteval's language support and limitations

    master

    Asteval supports a large subset of Python, including:

    • Control Flow: if-elif-else blocks, if expressions, for loops, while loops, with blocks, and try-except-finally blocks.
    • Data Structures: Dictionaries, tuples, lists, sets, strings, and NumPy nd-arrays.
    • Features: List comprehensions, slicing, subscripting, and f-strings.
    • Built-ins: Many standard Python built-ins and the math module.

    Intentional Limitations for Safety: To protect against dangerous user code, the following are not supported:

    • Creating classes.
    • Accessing internal methods/classes (especially __dunder__ methods).
    • eval, exec, yield, async, match/case, function decorators, generators, and type annotations.
    • t-strings (though f-strings are supported).
    • Importing modules (this is disabled by default but can be enabled).
    • Certain built-ins like getattr and setattr are disallowed.
    • Functions like open are replaced with safer versions.
  10. Configure Interpreter language features

    master

    You can enable or disable specific Python language constructs (AST nodes) to create a restricted or expanded language environment.

    Minimal Configuration

    Setting minimal=True disables most complex Python constructs (like if blocks, for loops, try/except, etc.) while maintaining support for basic data types, operators, and slicing. This is useful for creating a bare-bones mathematical language.

    Individual Node Control

    You can enable or disable specific nodes using no_NODE or with_NODE arguments, or by passing a config dictionary.

    Commonly controlled nodes include:

    • if, for, while, try, with (control blocks)
    • functiondef (defining functions)
    • lambda (lambda expressions)
    • listcomp, dictcomp, setcomp (comprehensions)
    • import, importfrom (module imports - disabled by default)

    To enable imports, use with_import=True and with_importfrom=True.

    from asteval import Interpreter
    
    # Use minimal configuration
    aeval_min = Interpreter(minimal=True)
    
    # Disable specific nodes via keyword arguments
    aeval_nowhile = Interpreter(no_while=True)
    
    # Disable specific nodes via a config dictionary
    config = {'while': False, 'if': False, 'try': False, 'for': False, 'with': False}
    aeval_noblocks = Interpreter(config=config)
    
    # Enable imports
    aeval_max = Interpreter(with_import=True, with_importfrom=True)
  11. Manage file access permissions in asteval

    master

    Asteval's default configuration allows disk access via Python's open() function, but it is restricted to read-only mode and operates with the permissions of the calling program.

    If you need to restrict or forbid disk access:

    • To forbid reading: Overwrite the open() function in the Interpreter symbol table with a custom implementation that restricts access to specific files or denies it entirely.
  12. Create and use an asteval Interpreter

    master

    To use asteval, import the Interpreter class and instantiate it. The resulting object provides an embedded, procedural, mathematical language environment that is very similar to Python. You can execute code by calling the interpreter instance with a string of code.

    Note that the default interpreter provides a limited but useful subset of Python. You can pass various options during initialization to control allowed functionality, pre-load data, or pre-load functions.

    from asteval import Interpreter
    aeval = Interpreter()
    
    # Execute simple expressions
    aeval('x = sqrt(3)')
    
    # Execute multi-line blocks
    aeval('''
    for i in range(10):
        print(i, sqrt(i), log(1+1))
    ''')