Documentation Index Fetch the complete documentation index at: https://mintlify.com/pydantic/monty/llms.txt
Use this file to discover all available pages before exploring further.
This guide will walk you through the basics of using Monty to execute Python code safely in your applications.
Choose Your Language
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
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
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 the runMontyAsync 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 ));
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