Metadata-Version: 2.4
Name: tiny-python
Version: 0.1.0
Summary: A safe execution environment for a Python-like DSL
Author-email: Gabe Montague <gabemontague@outlook.com>
License-File: LICENSE
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.8
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Topic :: Software Development :: Interpreters
Requires-Python: >=3.8
Provides-Extra: dev
Requires-Dist: pytest-cov; extra == 'dev'
Requires-Dist: pytest>=7.0; extra == 'dev'
Description-Content-Type: text/markdown

# tiny-python (pre-release)

**WARNING: This library is still under development and needs to be thoroughly tested for security.**

LLMs are very good at writing python code!

`tiny-python` allows you to take advantage of this without your web app getting hacked.

It is very simple: use `tiny_exec(unsafe_llm_code)` on code generated by an LLM, and a sandboxed python-like language will be run instead of dangerously running real python.

## Why?

- **More powerful structured outputs**: OpenAI supports [structured outputs](https://platform.openai.com/docs/guides/structured-outputs), but you must define the schema rigorously beforehand, and it is really only designed to handle small numbers of classes. What if you are having the AI create a complicated nested and polymorphic structure like video game level?  What if you want the AI to be able to use mathematical functions to lay out positions of where things are?

**Solution**: Have the LLM generate python and evaluate it safely using `tiny-python`. You can control the number of iterations of execution the code is allowed.

## Does tiny-python guarantee security?

**NO!**

`tiny-python` allows you to pass your own functions and dataclasses into the execution environment! You must use this feature responsibly. If you want to see a toy example of insecure usage of `tiny-python`:

```python
# BAD: Suppose we define an insecure dataclass
@dataclass
class MyInsecureClass:
  code: str
  def __post_init__(self):
    exec(self.code)  # exec is bad.

# BAD: Then we run on an LLM output
tiny_exec(llm_output, allowed_classes=[MyInsecureClass])
```

To use this library properly, you should avoid providing it access to dataclasses with insecure side effects. Care must be taken when you pass any of the following extra arguments to the `tiny_exec` (see more docs on these below):

- `allowed_classes`
- `allow_global_functions`
- `global_vars`
- `allow_dataclass_methods`

## Features

- Safe execution without using Python's `exec` or `eval`
- Support for math operations, string operations, and dataclass instantiation
- Control flow structures (if, for, while)
- Configurable execution limits (iterations, recursion depth)

## Installation

```bash
pip install tiny-python
```

## Quick Start

```python
from tiny_python import tiny_exec
from dataclasses import dataclass

# Basic math operations
result = tiny_exec("2 + 3 * 4")
print(result)  # 14

# String operations
result = tiny_exec("""
text = "hello"
result = text.upper() + " WORLD"
result
""")
print(result)  # "HELLO WORLD"

# Using dataclasses
@dataclass
class Point:
    x: float
    y: float

result = tiny_exec("""
p = Point(3, 4)
distance = (p.x ** 2 + p.y ** 2) ** 0.5
distance
""", allowed_classes=[Point])
print(result)  # 5.0

# Control flow
result = tiny_exec("""
total = 0
for i in range(5):
    if i % 2 == 0:
        total += i
total
""")
print(result)  # 6
```

## API

### `tiny_exec(code, **kwargs)`

Execute tiny-python code safely.

Parameters:

- `code`: String containing the code to execute
- `max_iterations`: Maximum number of iterations allowed (default: 10000)
- `max_recursion_depth`: Maximum recursion depth (default: 100)
- `allowed_classes`: List of classes that can be instantiated (must be dataclasses)
- `global_vars`: Dictionary of global variables to make available
- `allow_dataclass_methods`: Allow calling methods on dataclass instances (default: False)
- `allow_global_functions`: Allow calling functions from global_vars (default: False)

Returns the last expression value or explicit return value.

## Supported Operations

- Arithmetic: `+`, `-`, `*`, `/`, `//`, `%`, `**`
- Comparison: `==`, `!=`, `<`, `<=`, `>`, `>=`, `in`, `not in`, `is`, `is not`
- Boolean: `and`, `or`, `not`
- Built-in functions: `len`, `range`, `int`, `float`, `str`, `bool`, `list`, `dict`, `tuple`, `set`, `abs`, `min`, `max`, `sum`, `round`, `sorted`, `reversed`, `enumerate`, `zip`, `all`, `any`, `isinstance`, `type`
- String methods: `upper`, `lower`, `strip`, `lstrip`, `rstrip`, `split`, `join`, `replace`, `startswith`, `endswith`, `find`, `rfind`, `format`, `capitalize`, `title`, `isdigit`, `isalpha`, `isalnum`, `isspace`
- List methods: `append`, `extend`, `insert`, `remove`, `pop`, `clear`, `index`, `count`, `sort`, `reverse`, `copy`
- Dict methods: `keys`, `values`, `items`, `get`, `update`
- Control flow: `if`, `elif`, `else`, `for`, `while`, `break`, `continue`, `return`, `pass`
- Variable assignment: `=`, `+=`, `-=`, `*=`, `/=`, `//=`, `%=`, `**=`
- Tuple unpacking in assignments and for loops
- Dataclass instantiation (with `allowed_classes`)
- Custom functions (with `global_vars` and `allow_global_functions=True`)
- Dataclass methods (with `allow_dataclass_methods=True`)

## Security

This package is designed to provide a safe execution environment by:

- Using AST parsing instead of `exec` or `eval`
- Whitelisting allowed operations
- Preventing access to dangerous built-ins
- Limiting iteration and recursion
- Restricting module imports and file operations

## License

MIT
