Choose Your Language
- Python
- JavaScript
- Rust
Basic Expression Evaluation
Start by running a simple Python expression:import pydantic_monty
# Create interpreter with code
m = pydantic_monty.Monty('1 + 2')
# Execute and get result
result = m.run()
print(result)
# Output: 3
Using Input Variables
Pass variables to your code:import pydantic_monty
# Define code that uses input variables
m = pydantic_monty.Monty('x * y', inputs=['x', 'y'])
# Run with different inputs
print(m.run(inputs={'x': 2, 'y': 3}))
# Output: 6
print(m.run(inputs={'x': 10, 'y': 5}))
# Output: 50
External Functions
Call host functions from your sandboxed code:import pydantic_monty
# Code that calls an external function
m = pydantic_monty.Monty('double(x)', inputs=['x'])
# Provide the external function implementation
result = m.run(
inputs={'x': 5},
external_functions={'double': lambda x: x * 2}
)
print(result)
# Output: 10
Iterative Execution
For fine-grained control over external function calls:import pydantic_monty
code = """
data = fetch(url)
len(data)
"""
m = pydantic_monty.Monty(code, inputs=['url'])
# Start execution - pauses at fetch() call
result = m.start(inputs={'url': 'https://example.com'})
print(type(result))
# Output: <class 'pydantic_monty.FunctionSnapshot'>
print(result.function_name)
# Output: fetch
print(result.args)
# Output: ('https://example.com',)
# Perform the actual fetch, then resume
result = result.resume(return_value='hello world')
print(type(result))
# Output: <class 'pydantic_monty.MontyComplete'>
print(result.output)
# Output: 11
Async External Functions
Use async/await with external functions:import asyncio
from typing import Any
import pydantic_monty
code = """
async def agent(prompt: str, messages: Messages):
while True:
print(f'messages so far: {messages}')
output = await call_llm(prompt, messages)
if isinstance(output, str):
return output
messages.extend(output)
await agent(prompt, [])
"""
type_definitions = """
from typing import Any
Messages = list[dict[str, Any]]
async def call_llm(prompt: str, messages: Messages) -> str | Messages:
raise NotImplementedError()
prompt: str = ''
"""
m = pydantic_monty.Monty(
code,
inputs=['prompt'],
script_name='agent.py',
type_check=True,
type_check_stubs=type_definitions,
)
Messages = list[dict[str, Any]]
async def call_llm(prompt: str, messages: Messages) -> str | Messages:
if len(messages) < 2:
return [{'role': 'system', 'content': 'example response'}]
else:
return f'example output, message count {len(messages)}'
async def main():
output = await pydantic_monty.run_monty_async(
m,
inputs={'prompt': 'testing'},
external_functions={'call_llm': call_llm},
)
print(output)
# Output: example output, message count 2
asyncio.run(main())
Resource Limits
Control resource usage to prevent runaway execution:import pydantic_monty
m = pydantic_monty.Monty('x + y', inputs=['x', 'y'])
# Set resource limits
limits = pydantic_monty.ResourceLimits(
max_duration_secs=1.0,
max_allocations=10000,
max_memory=1024 * 1024 # 1MB
)
result = m.run(
inputs={'x': 1, 'y': 2},
limits=limits
)
print(result)
# Output: 3
Basic Expression Evaluation
Start by running a simple Python expression:import { Monty } from '@pydantic/monty'
// Create interpreter and run code
const m = new Monty('1 + 2')
const result = m.run()
console.log(result)
// Output: 3
Using Input Variables
Pass variables to your code:import { Monty } from '@pydantic/monty'
const m = new Monty('x + y', { inputs: ['x', 'y'] })
const result = m.run({ inputs: { x: 10, y: 20 } })
console.log(result)
// Output: 30
External Functions
Call host functions from your sandboxed code:import { Monty } from '@pydantic/monty'
const m = new Monty('add(2, 3)')
const result = m.run({
externalFunctions: {
add: (a: number, b: number) => a + b,
},
})
console.log(result)
// Output: 5
Async External Functions
Use async functions with therunMontyAsync helper:import { Monty, runMontyAsync } from '@pydantic/monty'
const m = new Monty('fetch_data(url)', {
inputs: ['url'],
})
const result = await runMontyAsync(m, {
inputs: { url: 'https://example.com' },
externalFunctions: {
fetch_data: async (url: string) => {
const response = await fetch(url)
return response.text()
},
},
})
console.log(result)
Iterative Execution
For fine-grained control over external function calls:import { Monty, MontySnapshot } from '@pydantic/monty'
const m = new Monty('a() + b()')
let progress = m.start()
while (progress instanceof MontySnapshot) {
console.log(`Calling: ${progress.functionName}`)
console.log(`Args: ${progress.args}`)
// Provide the return value and resume
progress = progress.resume({ returnValue: 10 })
}
// progress is now MontyComplete
console.log(progress.output)
// Output: 20
Resource Limits
Control resource usage to prevent runaway execution:import { Monty } from '@pydantic/monty'
const m = new Monty('1 + 1')
const result = m.run({
limits: {
maxAllocations: 10000,
maxDurationSecs: 5,
maxMemory: 1024 * 1024, // 1MB
maxRecursionDepth: 100,
},
})
console.log(result)
// Output: 2
Error Handling
Handle different types of errors:import {
Monty,
MontySyntaxError,
MontyRuntimeError,
MontyTypingError
} from '@pydantic/monty'
try {
const m = new Monty('1 / 0')
m.run()
} catch (error) {
if (error instanceof MontySyntaxError) {
console.log('Syntax error:', error.message)
} else if (error instanceof MontyRuntimeError) {
console.log('Runtime error:', error.message)
console.log('Traceback:', error.traceback())
} else if (error instanceof MontyTypingError) {
console.log('Type error:', error.displayDiagnostics())
}
}
Basic Expression Evaluation
Start by running a simple Python expression:use monty::{MontyRun, MontyObject, NoLimitTracker, PrintWriter};
let code = "1 + 2";
let runner = MontyRun::new(
code.to_owned(),
"main.py",
vec![]
).unwrap();
let result = runner.run(
vec![],
NoLimitTracker,
&mut PrintWriter::Stdout
).unwrap();
assert_eq!(result, MontyObject::Int(3));
Using Input Variables
Pass variables to your code:use monty::{MontyRun, MontyObject, NoLimitTracker, PrintWriter};
let code = "x + y";
let runner = MontyRun::new(
code.to_owned(),
"main.py",
vec!["x".to_owned(), "y".to_owned()]
).unwrap();
let result = runner.run(
vec![MontyObject::Int(10), MontyObject::Int(20)],
NoLimitTracker,
&mut PrintWriter::Stdout
).unwrap();
assert_eq!(result, MontyObject::Int(30));
Fibonacci Example
A more complex example with function definitions:use monty::{MontyRun, MontyObject, NoLimitTracker, PrintWriter};
let code = r#"
def fib(n):
if n <= 1:
return n
return fib(n - 1) + fib(n - 2)
fib(x)
"#;
let runner = MontyRun::new(
code.to_owned(),
"fib.py",
vec!["x".to_owned()]
).unwrap();
let result = runner.run(
vec![MontyObject::Int(10)],
NoLimitTracker,
&mut PrintWriter::Stdout
).unwrap();
assert_eq!(result, MontyObject::Int(55));
Serialization
Save and restore parsed code:use monty::{MontyRun, MontyObject, NoLimitTracker, PrintWriter};
// Serialize parsed code
let runner = MontyRun::new(
"x + 1".to_owned(),
"main.py",
vec!["x".to_owned()]
).unwrap();
let bytes = runner.dump().unwrap();
// Later, restore and run
let runner2 = MontyRun::load(&bytes).unwrap();
let result = runner2.run(
vec![MontyObject::Int(41)],
NoLimitTracker,
&mut PrintWriter::Stdout
).unwrap();
assert_eq!(result, MontyObject::Int(42));
Key Concepts
Inputs
Variables you want to pass into the sandboxed code. Declare them when creating the interpreter.
External Functions
Host functions that sandboxed code can call. These are the only way for Monty code to interact with the outside world.
Resource Limits
Control execution time, memory usage, and allocations to prevent runaway code.
Snapshots
Pause execution at external function calls and resume later, even across process boundaries.
Common Patterns
Agent Workflow Example
Here’s a real-world example of using Monty for an agent workflow:import asyncio
from typing import Any
import pydantic_monty
Messages = list[dict[str, Any]]
code = """
async def agent(prompt: str):
messages = []
while True:
output = await call_llm(prompt, messages)
if isinstance(output, str):
return output
messages.extend(output)
await agent(prompt)
"""
m = pydantic_monty.Monty(
code,
inputs=['prompt'],
script_name='agent.py'
)
async def call_llm(prompt: str, messages: Messages) -> str | Messages:
# Your LLM logic here
if len(messages) < 2:
return [{'role': 'system', 'content': 'response'}]
else:
return f'Final answer based on {len(messages)} messages'
async def main():
result = await pydantic_monty.run_monty_async(
m,
inputs={'prompt': 'What is the weather?'},
external_functions={'call_llm': call_llm}
)
print(result)
asyncio.run(main())
Next Steps
Core Concepts
Learn more about Monty’s core features
External Functions
Deep dive into external function handling
Resource Limits
Configure resource limits for safety
Type Checking
Use static type checking with your code
