Metadata-Version: 2.5
Name: leval
Version: 1.4.0
Summary: Limited evaluator
Project-URL: Homepage, https://github.com/valohai/leval
Author-email: Valohai <info@valohai.com>
License-Expression: MIT
License-File: LICENSE
Requires-Python: >=3.8
Description-Content-Type: text/markdown

# Leval

![License](https://img.shields.io/github/license/valohai/leval)
[![Coverage](https://img.shields.io/codecov/c/github/valohai/leval)](https://app.codecov.io/gh/valohai/leval)
[![CI](https://github.com/valohai/leval/actions/workflows/ci.yml/badge.svg)](https://github.com/valohai/leval/actions/workflows/ci.yml)
[![PyPI](https://img.shields.io/pypi/v/leval)](https://pypi.org/project/leval)

> A limited expression evaluator

A little more suited for dynamic usage than `ast.literal_eval()`
while remaining as safe as the functions you pass in.

Under the hood, it uses the `ast` module to parse the expression,
then walks the AST in Python to evaluate the result. You can
also specify a depth limit for the complexity of the expression,
as well as a time limit for the evaluation.

## Example usage

### Simple API

For many use cases, the `simple_eval()` function is sufficient.
You can specify depth, time and expression size limits,
and optional mappings of variables and functions.

The `values` mapping can also be keyed by a tuple of strings, which
is what attribute accesses are folded to.

Operations are generally limited to numbers only in the simple API.

```python
from leval.simple import simple_eval

assert simple_eval('1 + 2') == 3
assert simple_eval('x < -80 or x > 125 or x == 85', values={'x': 85})
assert simple_eval('abs(x) > 80', values={'x': -85}, functions={'abs': abs})
assert simple_eval('x.y.z + 8', values={('x', 'y', 'z'): 34}) == 42
```

### Advanced API

Under the hood, `simple_eval` simply

1. initializes an evaluation universe, which defines the functions, variables
   and operations available
2. creates an Evaluator to evaluate the expression with the given universe

Both of these classes are designed to be easily subclassable. There are examples
in the `test_leval.py` file.

## Security

`leval` walks the AST itself and never uses `getattr`, subscripting, or calls to
anything other than the functions you explicitly register by name.

The evaluator is only as safe as what you put into the universe,
so keep the following in mind:

- **You own the `functions` and `values`.** Any function you register can be
  called with attacker-controlled arguments, and operators (`==`, `<`, `in`,
  arithmetic, …) invoke the corresponding dunder methods on the values you
  provide. Do not register anything with side effects (`open`, `eval`, importers,
  ORM objects, …) or pass in objects whose comparisons/arithmetic do something
  dangerous.
- **`max_time` is cooperative, not preemptive.** It is only checked between AST
  node visits, so a single slow function call or a single expensive operation
  runs to completion regardless of the limit. Enforce hard timeouts out of
  process if you need them.
- **Exceptions are not all wrapped.** Errors from operators and registered
  functions (e.g. `ZeroDivisionError`, `TypeError`, `ValueError`) propagate as
  their native types, not only as `leval.excs.EvaluatorError`. Catch broadly
  around evaluation.
