Skip to content

Latest commit

 

History

546 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

interpretthis

Sandboxed Python AST interpreter for Rust, Python, and TypeScript.

cargo add interpretthis     # crates.io
pip install interpretthis   # PyPI
npm install interpretthis   # npm

Runs untrusted or LLM-generated Python by evaluating a rustpython-parser AST under resource limits and an allowlisted language surface. It is not CPython embedded via the C API: there is no filesystem, network, or process access unless the host injects those as tools.

Why

You need to execute model-written Python for structured work (transforms, scoring, tool-orchestrated agents) without giving the model a real Python process. interpretthis is the evaluator: host code owns tools, limits, and what happens with the result.

Quick start

use std::collections::HashMap;

use interpretthis::{
    Interpreter, InterpreterConfig, InterpreterDeps, KwargsExt, ToolDefinition, Tools, Value,
};

#[tokio::main]
async fn main() {
    let tools = Tools::new().with(
        "double",
        ToolDefinition::from_fn(|kwargs| async move {
            let n = kwargs.require_int("n")?;
            Ok(Value::Int(n * 2))
        }),
    );

    let interp = Interpreter::new(
        InterpreterDeps { tools },
        InterpreterConfig::default(),
    );

    // Per-call tools merge with registered tools (per-call wins on name clash).
    let resp = interp
        .execute(
            "result = double(n=x)\nprint(result)",
            &Tools::new(),
            HashMap::from([("x".to_string(), Value::Int(42))]),
        )
        .await;

    match resp.result() {
        Ok(()) => println!("{}", resp.stdout),
        Err(e) => eprintln!("{e}"),
    }
}

Requires Rust 1.88+ and Tokio.

From Python

from interpretthis import Interpreter

interp = Interpreter(tools={"double": lambda n: n * 2})
result = interp.execute("answer = double(n=x)\nprint(answer)", {"x": 21})

result.stdout                  # '42\n'
interp.get_variable("answer")  # 42

Tools may be async def; from async code use await interp.execute_async(...), and tool coroutines run on your own event loop. See crates/interpretthis-python/README.md.

From TypeScript

import { Interpreter } from 'interpretthis'

const interp = new Interpreter({ double: ({ n }) => n * 2 })
const result = await interp.execute('answer = double(n=x)\nprint(answer)', { x: 21 })

result.stdout                    // '42\n'
interp.getVariable('answer')     // 42

Tools may be async. See crates/interpretthis-node/README.md.

In every language, a failing run is data, not an exception: you get stdout and the error, because a script that prints and then breaks has told you something useful in both — and that pair is what you feed back to a model.

Limits (honest)

  • Language subset — large, but not full CPython. See CONFORMANCE.md.
  • Sandbox — dangerous names/attrs and non-allowlisted imports are rejected. See THREAT_MODEL.md.
  • Tools are trusted — a host tool with side effects extends the trust boundary. Tool failures become a generic Python Exception (catchable); uncaught they fail the host execute call.
  • State export is a versioned byte blob (4-byte version + JSON) for host-owned resume; signing/encryption is the host’s job.

Docs

docs.rs/interpretthis API reference (after crates.io publish)
CONFORMANCE.md CPython divergences
STATUS.md Parity track status
THREAT_MODEL.md Security boundary
CHANGELOG.md Release notes
CONTRIBUTING.md Contribution policy (outreach first)
RELEASING.md Tag and crates.io publish
MODULE_TEMPLATE.md Adding a stdlib module
SECURITY.md Vulnerability reporting

Development

cargo nextest run --all-targets   # preferred; cargo test also works
cargo clippy --all-targets -- -D warnings
scripts/license-headers.sh check

Parity tests compare against host python3.12 when available (crates/interpretthis/tests/integration/parity_corpus/).

License

Licensed under either of

at your option.

Contribution policy

We do not accept unsolicited outside contributions. If you are interested in working on this project, email opensource@moderately.ai and explain why — see CONTRIBUTING.md.

If a contribution is later accepted, unless you explicitly state otherwise it is dual-licensed under MIT OR Apache-2.0 as above, without additional terms.

About

Sandboxed Python AST interpreter for untrusted and LLM-generated code

Topics

Resources

Contributing

Security policy

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages