The Problem
LLMs often need to execute short Python snippets safely and with minimal latency. Traditional container‑based sandboxes add seconds of startup overhead and expose a large attack surface. A lightweight interpreter that can be embedded in Rust, Python, or JavaScript and that enforces strict host‑resource isolation fills that gap.
What This Does
monty is a minimal, secure Python interpreter written in Rust. The core interpreter lives in crates/monty/src/ and implements the bytecode VM, built‑ins, and a small standard‑library subset (sys, typing, asyncio, dataclasses, json).
- The CLI entry point
crates/monty-cli/src/main.rsbuilds a binary that parses command‑line arguments (crates/monty/src/args.rs) and invokesrun(crates/monty/src/run.rs). - Language bindings are provided for JavaScript (
crates/monty-js/src/lib.rs) and Python (crates/monty-python/src/lib.rs), each exposing a thin wrapper around the same core VM. - Type‑checking is delegated to the
monty-type-checkingcrate, which ships thetytool and runs static analysis on the supplied code.
How It Is Wired
Execution starts at crates/monty-cli/src/main.rs:11 (main). main parses arguments, then calls run in crates/monty/src/run.rs:85. run creates a fresh interpreter state, loads the bytecode compiler (crates/monty/src/bytecode/compiler.rs), and invokes run_no_limits, which walks the VM (crates/monty/src/bytecode/vm/mod.rs).
Key hot‑spots:
| Function | Calls From | Calls To | Reach |
|---|---|---|---|
Err | 224 places | – | Central error handling |
drop_with_heap | 158 places | many heap ops | Memory management hub |
new_msg | 117 places | message construction | Serialization path |
run_no_limits | 81 places | VM execution | Core interpreter loop |
py_call_attr | 30 places | drop_with_heap, Err, allocate | Attribute dispatch |
The only external effect is a filesystem write performed by write_zipped_typeshed_to (called from main) which opens a File handle. No network or env‑var access is present in the current code base.
Responsibility mapping (most traversed files)
crates/monty/src/exception_private.rs– defines core exception helpers (attribute_error,call).crates/monty/src/args.rs– argument validation utilities (check_zero_args,get_one_arg).crates/monty/src/builtins/mod.rs– registration of built‑ins (py_type,py_repr_fmt).crates/monty/src/heap.rs– garbage‑collection and reference tracking.crates/monty/src/types/*– concrete Python types (str,list,set,bytes).
The import graph shows 423 internal modules with only 12 import edges and no cycles, indicating a clean dependency direction. The most‑connected module is the JS wrapper (crates/monty-js/wrapper) which is imported by ten other modules but does not import any, making it a low‑risk change point.
How To Use It
# Clone the repo
git clone https://github.com/moses-y/monty
cd monty
# Build the Rust binaries (core + CLI)
cargo build --release # produces target/release/monty-cli
# Run a Python snippet via the CLI
./target/release/monty-cli run -c "print('hello')" # -c flag is defined in args.rs
# (Optional) Build the JS bindings
cd crates/monty-js
npm ci # lockfile‑based install
npm run build # produces a Node module exposing `runMonty`
Configuration is limited to the command‑line flags defined in crates/monty/src/args.rs. No external config files, environment variables, or secret handling are required.
Real‑World Use
A backend service that receives LLM‑generated Python actions can embed the compiled monty-cli binary (or the Rust library monty) and invoke run with a strict time‑ and memory‑budget. The interpreter returns captured stdout/stderr and any raised Monty exceptions, allowing the service to safely surface the LLM’s intent without a full container.
Code Health & Issues
Measured static‑analysis findings (90 total)
- High – Cognitive load – Deep nesting (max depth 9) in
crates/monty/src/args.rs,bytecode/vm/async_exec.rs,bytecode/vm/mod.rs. - High – Clarity – Repeated 6‑line blocks across 52 built‑ins (e.g.,
abs.rs,divmod.rs,all.rs). - High – Oversized files –
compiler.rs,heap.rs,parse.rseach > 1 700 lines. - Medium – Resource safety – Unwrapped
openincrates/monty-python/python/pydantic_monty/os_access.py. - Medium – Resilience – Broad
except:clauses in several test cases (ext_call__ext_exc.py, etc.).
Repository‑level health audit
- HIGH – GitHub Actions use tag references (
@vN). Pin each action to a 40‑char SHA. - MEDIUM – No Dependabot/Renovate config; add
.github/dependabot.yml. - MEDIUM – CI runs
npm installinstead ofnpm ci; switch to lockfile‑strict install. - MEDIUM – No dependency‑vulnerability scan in CI; add
dependency-review-actionorosv-scanner. - MEDIUM – Checkout step retains the token; set
persist-credentials: falseand pass token only where needed. - LOW – Workflow jobs lack
timeout-minutes; add a reasonable timeout.
Other hygiene: tests and CI are present; licence (LICENSE) and lockfiles (Cargo.lock, package-lock.json) are committed; no secrets detected.
The Bottom Line
monty delivers a fast, sandboxed Python runtime that can be embedded in multiple host languages, making it a practical choice for LLM‑driven code execution. However, the codebase suffers from deep nesting, duplicated built‑in logic, and several oversized modules that increase maintenance risk. Teams comfortable with Rust and willing to refactor hot spots will find it usable; otherwise the current technical debt may outweigh the performance benefits.