Embedded Python (Monty)
Experimental. Monty is an early-stage Python interpreter that may have undiscovered crash or security bugs. Resource limits are enforced by Monty’s runtime. The integration should be treated as experimental.
Bashkit embeds the Monty Python interpreter, a pure-Rust implementation of Python 3.12. Python runs entirely in-memory with configurable resource limits and no host access.
See also:
- Threat Model - Security considerations (TM-PY-*)
- Custom Builtins - Writing your own builtins
- Compatibility Reference - Bash feature support
specs/python-builtin.md- Full specification
Quick Start
Enable the python feature and register via builder:
use bashkit::Bash;
let mut bash = Bash::builder()
.python()
.env("BASHKIT_ALLOW_INPROCESS_PYTHON", "1")
.build();
let result = bash.exec("python3 -c \"print('hello from Monty')\"").await?;
assert_eq!(result.stdout, "hello from Monty\n");
Usage Patterns
Inline Code
python3 -c "print(2 ** 10)"
# Output: 1024
Expression Evaluation
When no print() is called, the last expression is displayed (REPL behavior):
python3 -c "2 + 2"
# Output: 4
Script Files (from VFS)
cat > /tmp/script.py << 'EOF'
data = [1, 2, 3, 4, 5]
print(f"sum={sum(data)}, avg={sum(data)/len(data)}")
EOF
python3 /tmp/script.py
Pipelines and Command Substitution
result=$(python3 -c "print(42 * 3)")
echo "Result: $result"
echo "print('piped')" | python3
Virtual Filesystem (VFS) Bridging
Python open() and pathlib.Path operations are bridged to Bashkit’s virtual
filesystem. Files created by bash are readable from Python and vice versa.
Bash → Python
echo "important data" > /tmp/shared.txt
python3 -c "
content = open('/tmp/shared.txt').read()
print(f'Got: {content.strip()}')
"
Python → Bash
python3 -c "
with open('/tmp/result.txt', 'w') as f:
_ = f.write('computed by python\n')
"
cat /tmp/result.txt
Supported File Operations
| Operation | Example |
|---|---|
| Open/read | open('f.txt').read() |
| Open/write | open('f.txt', 'w').write('data') |
| Open/append | open('f.txt', 'a').write('more') |
| Path open | Path('f.txt').open('r') |
| Read text | Path('f.txt').read_text() |
| Read bytes | Path('f.txt').read_bytes() |
| Write text | Path('f.txt').write_text('data') |
| Write bytes | Path('f.txt').write_bytes(b'data') |
| Exists | Path('f.txt').exists() |
| Is file/dir | Path('f.txt').is_file(), .is_dir() |
| Mkdir | Path('d').mkdir(parents=True, exist_ok=True) |
| Delete | Path('f.txt').unlink() |
| List dir | Path('.').iterdir() |
| Stat | Path('f.txt').stat().st_size |
| Rename | Path('old').rename('new') |
| Env vars | os.getenv('KEY'), os.environ |
Architecture
Python code → Monty VM → OsCall(Open/ReadText, path) → Bashkit VFS → resume
Monty pauses at filesystem operations, Bashkit bridges them to the VFS, then
resumes execution with the result (or a Python exception like FileNotFoundError).
Resource Limits
Default limits prevent runaway Python code. Customize via PythonLimits:
use bashkit::{Bash, PythonLimits};
use std::time::Duration;
let bash = Bash::builder()
.python_with_limits(
PythonLimits::default()
.max_duration(Duration::from_secs(5))
.max_memory(16 * 1024 * 1024) // 16 MB
.max_allocations(100_000)
.max_recursion(50)
)
.build();
| Limit | Default | Purpose |
|---|---|---|
| Allocations | 1,000,000 | Heap allocation cap |
| Duration | 30 seconds | Execution timeout |
| Memory | 64 MB | Heap memory cap |
| Recursion | 200 | Call stack depth |
LLM Tool Integration
When using BashTool for AI agents, call .python() on the tool builder:
use bashkit::{BashTool, Tool};
let tool = BashTool::builder()
.python()
.build();
// help() and system_prompt() automatically document Python limitations
let help = tool.help(); // Includes a Markdown Notes section with Python hints
The builtin’s llm_hint() is automatically included in the tool’s documentation,
so LLMs know file I/O is VFS-scoped and HTTP requests/classes are unavailable.
Limitations
VFS-only file I/O. open() and pathlib.Path read/write Bashkit’s virtual
filesystem, not the host filesystem:
from pathlib import Path
with open('/tmp/data.txt', 'w') as f:
f.write('hello')
content = Path('/tmp/data.txt').read_text()
No HTTP/network. No socket, urllib, requests, or http.client modules.
Monty has no network primitives and no OsCall variants for network operations.
No classes. Class definitions are not yet supported by Monty (planned upstream).
No third-party imports. Only builtin modules (sys, typing, os, pathlib,
math, json, datetime) are available. No pip install, no import numpy.
No re module. Disabled due to catastrophic backtracking DoS risk in
untrusted code execution.
No str.format(). Use f-strings instead: f"value={x}" not "value={}".format(x).
Security
All Python execution runs in a virtual environment:
- No host filesystem access — all paths resolve through the VFS
- No network access — no sockets, HTTP, or DNS
- No process spawning — no
os.system(),subprocess, or__import__('os') - Resource limited — allocation, time, memory, and recursion caps
- Path traversal safe —
../..is resolved by VFS path normalization
See threat IDs TM-PY-001 through TM-PY-029 in the threat model.