Skip to main content

Overview

Monty supports iterative execution, allowing you to pause and resume Python code execution. This is useful for:
  • Handling external function calls with custom logic
  • Implementing async external functions
  • Debugging and step-through execution
  • Dynamic name resolution
When you call Monty.start() or resume() on a snapshot, execution returns one of three types:
  • MontySnapshot - Paused at an external function call
  • MontyNameLookup - Paused at an undefined name lookup
  • MontyComplete - Execution finished successfully

MontySnapshot

Represents paused execution waiting for an external function call to be resolved.

Properties

scriptName
string
The name of the script being executed
functionName
string
The name of the external function being called
args
any[]
Positional arguments passed to the external function
kwargs
Record<string, any>
Keyword arguments passed to the external function as an object

Methods

resume()

resume(options: ResumeOptions): MontySnapshot | MontyNameLookup | MontyComplete
Resumes execution with either a return value or an exception.
options
ResumeOptions
required
Must contain either returnValue or exception, but not both
result
MontySnapshot | MontyNameLookup | MontyComplete
  • MontySnapshot - Paused at another external function call
  • MontyNameLookup - Paused at a name lookup
  • MontyComplete - Execution completed
Throws:
  • MontyRuntimeError - If the code raises an exception

dump()

dump(): Buffer
Serializes the snapshot to binary format for later restoration.
data
Buffer
Binary representation of the snapshot

load() (static)

static load(data: Buffer, options?: SnapshotLoadOptions): MontySnapshot
Deserializes a snapshot from binary format.
data
Buffer
required
Binary data from dump()
options
SnapshotLoadOptions
Optional configuration for loading
snapshot
MontySnapshot
Restored snapshot ready to resume

Example: Basic Iteration

import { Monty, MontySnapshot, MontyComplete } 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)
  console.log(`Kwargs:`, progress.kwargs)
  
  // Provide the return value and resume
  progress = progress.resume({ returnValue: 10 })
}

// progress is now MontyComplete
if (progress instanceof MontyComplete) {
  console.log('Result:', progress.output) // 20
}

Example: Error Injection

const m = new Monty('try_operation()')

let progress = m.start()
if (progress instanceof MontySnapshot) {
  // Inject an error instead of a return value
  progress = progress.resume({
    exception: {
      type: 'ValueError',
      message: 'Operation failed',
    },
  })
}

Example: Serialization

const m = new Monty('external_func()')

const snapshot = m.start()
if (snapshot instanceof MontySnapshot) {
  // Save snapshot
  const data = snapshot.dump()
  fs.writeFileSync('snapshot.bin', data)
  
  // Later, restore and resume
  const restored = MontySnapshot.load(data)
  const result = restored.resume({ returnValue: 42 })
}

MontyNameLookup

Represents paused execution waiting for a name (variable) to be resolved.

Properties

scriptName
string
The name of the script being executed
variableName
string
The name of the variable being looked up

Methods

resume()

resume(options?: NameLookupResumeOptions): MontySnapshot | MontyNameLookup | MontyComplete
Resumes execution after resolving the name lookup.
options
NameLookupResumeOptions
result
MontySnapshot | MontyNameLookup | MontyComplete
  • MontySnapshot - Paused at an external function call
  • MontyNameLookup - Paused at another name lookup
  • MontyComplete - Execution completed

dump() / load()

Same as MontySnapshot - serialize and deserialize name lookup state.

Example: Dynamic Name Resolution

import { Monty, MontyNameLookup, MontySnapshot } from '@pydantic/monty'

const externalFunctions = {
  add: (a: number, b: number) => a + b,
  multiply: (a: number, b: number) => a * b,
}

const m = new Monty('add(2, 3) + multiply(4, 5)')

let progress = m.start()
while (!(progress instanceof MontyComplete)) {
  if (progress instanceof MontyNameLookup) {
    // Resolve the name to an external function
    const name = progress.variableName
    const func = externalFunctions[name]
    
    if (func) {
      progress = progress.resume({ value: func })
    } else {
      // Raise NameError by resuming with no value
      progress = progress.resume()
    }
  } else if (progress instanceof MontySnapshot) {
    // Handle function call
    const func = externalFunctions[progress.functionName]
    const result = func(...progress.args)
    progress = progress.resume({ returnValue: result })
  }
}

console.log(progress.output) // 25

MontyComplete

Represents successfully completed execution with a final output value.

Properties

output
any
The final result value from the executed code (the result of the last expression)

Example

import { Monty, MontyComplete } from '@pydantic/monty'

const m = new Monty('1 + 2 + 3')
const result = m.start()

if (result instanceof MontyComplete) {
  console.log('Result:', result.output) // 6
}

Complete Example: Async External Functions

Combining snapshots with async functions:
import { Monty, MontySnapshot, MontyNameLookup, MontyComplete } from '@pydantic/monty'

const externalFunctions = {
  fetch_data: async (url: string) => {
    const response = await fetch(url)
    return response.json()
  },
  process: (data: any) => {
    return data.value * 2
  },
}

async function runAsync(m: Monty, inputs?: Record<string, any>) {
  let progress = m.start({ inputs })
  
  while (!(progress instanceof MontyComplete)) {
    if (progress instanceof MontyNameLookup) {
      // Resolve name to external function
      const func = externalFunctions[progress.variableName]
      progress = progress.resume({ value: func })
    } else if (progress instanceof MontySnapshot) {
      // Call external function
      const func = externalFunctions[progress.functionName]
      try {
        let result = func(...progress.args, progress.kwargs)
        
        // Await if promise
        if (result && typeof result.then === 'function') {
          result = await result
        }
        
        progress = progress.resume({ returnValue: result })
      } catch (error) {
        progress = progress.resume({
          exception: {
            type: 'RuntimeError',
            message: error.message,
          },
        })
      }
    }
  }
  
  return progress.output
}

const code = `
data = fetch_data('https://api.example.com/data')
result = process(data)
result
`

const m = new Monty(code)
const result = await runAsync(m)
console.log(result)
For simple async function support, use the built-in runMontyAsync() helper instead of manually handling snapshots. See Monty Class.

See Also

Build docs developers (and LLMs) love