Skip to content

Commit 9b8aa64

Browse files
authored
Fix monty-js readme (#92)
1 parent 3a5a93c commit 9b8aa64

File tree

2 files changed

+210
-32
lines changed

2 files changed

+210
-32
lines changed

CLAUDE.md

Lines changed: 19 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -382,17 +382,30 @@ The JavaScript package provides Node.js bindings for the Monty interpreter via n
382382

383383
### Current API
384384

385-
The package currently exposes a single function:
385+
The package exposes:
386+
387+
- `Monty` class - Parse and execute Python code with inputs, external functions, and resource limits
388+
- `MontySnapshot` / `MontyComplete` - For iterative execution with `start()` / `resume()`
389+
- `runMontyAsync()` - Helper for async external functions
390+
- `MontySyntaxError` / `MontyRuntimeError` / `MontyTypingError` - Error classes
386391

387392
```ts
388-
function run(code: string): RunResult
393+
import { Monty, MontySnapshot, runMontyAsync } from '@pydantic/monty'
394+
395+
// Basic execution
396+
const m = new Monty('x + 1', { inputs: ['x'] })
397+
const result = m.run({ inputs: { x: 10 } }) // returns 11
389398

390-
interface RunResult {
391-
output: string // Captured print() output
392-
result: string // Debug representation of final value
399+
// Iterative execution for external functions
400+
const m2 = new Monty('fetch(url)', { inputs: ['url'], externalFunctions: ['fetch'] })
401+
let progress = m2.start({ inputs: { url: 'https://...' } })
402+
if (progress instanceof MontySnapshot) {
403+
progress = progress.resume({ returnValue: 'response data' })
393404
}
394405
```
395406

407+
See `crates/monty-js/README.md` for full API documentation.
408+
396409
### Building and Testing
397410

398411
```bash
@@ -428,14 +441,4 @@ npm test
428441

429442
- Tests use [ava](https://github.com/avajs/ava) and live in `crates/monty-js/__test__/`
430443
- Tests are written in TypeScript
431-
- Follow the existing test style in `index.spec.ts`
432-
433-
### Future Work
434-
435-
The JS bindings currently only expose a simple `run()` function. Future work may expose:
436-
- Input variables
437-
- Resource limits
438-
- External functions
439-
- Snapshot/resume (iterative execution)
440-
441-
These features mirror the Python package API and are implemented in the Rust core.
444+
- Follow the existing test style in the `__test__/` directory

crates/monty-js/README.md

Lines changed: 191 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,35 +1,210 @@
11
# @pydantic/monty
22

3-
JavaScript bindings for the Monty sandboxed Python interpreter.
3+
JavaScript/TypeScript bindings for the Monty sandboxed Python interpreter.
44

55
## Installation
66

77
```bash
88
npm install @pydantic/monty
99
```
1010

11-
## Usage (CommonJS)
11+
## Basic Usage
1212

13-
```js
14-
const monty = require('@pydantic/monty')
13+
```ts
14+
import { Monty } from '@pydantic/monty'
15+
16+
// Create interpreter and run code
17+
const m = new Monty('1 + 2')
18+
const result = m.run() // returns 3
19+
```
20+
21+
## Input Variables
22+
23+
```ts
24+
const m = new Monty('x + y', { inputs: ['x', 'y'] })
25+
const result = m.run({ inputs: { x: 10, y: 20 } }) // returns 30
26+
```
1527

16-
const { output, result } = monty.run('print("hello")\n1 + 2')
17-
console.log(output) // "hello\n"
18-
console.log(result) // debug representation of the final value
28+
## External Functions
29+
30+
For synchronous external functions, pass them directly to `run()`:
31+
32+
```ts
33+
const m = new Monty('add(2, 3)', { externalFunctions: ['add'] })
34+
35+
const result = m.run({
36+
externalFunctions: {
37+
add: (a: number, b: number) => a + b,
38+
},
39+
}) // returns 5
1940
```
2041

21-
## Usage (ESM / TypeScript)
42+
For async external functions, use `runMontyAsync()`:
2243

2344
```ts
24-
import monty from '@pydantic/monty'
45+
import { Monty, runMontyAsync } from '@pydantic/monty'
2546

26-
const res = monty.run('print("hi")\n3 * 7')
27-
console.log(res.output)
28-
console.log(res.result)
47+
const m = new Monty('fetch_data(url)', {
48+
inputs: ['url'],
49+
externalFunctions: ['fetch_data'],
50+
})
51+
52+
const result = await runMontyAsync(m, {
53+
inputs: { url: 'https://example.com' },
54+
externalFunctions: {
55+
fetch_data: async (url: string) => {
56+
const response = await fetch(url)
57+
return response.text()
58+
},
59+
},
60+
})
2961
```
3062

31-
## API
63+
## Iterative Execution
64+
65+
For fine-grained control over external function calls, use `start()` and `resume()`:
66+
67+
```ts
68+
const m = new Monty('a() + b()', { externalFunctions: ['a', 'b'] })
69+
70+
let progress = m.start()
71+
while (progress instanceof MontySnapshot) {
72+
console.log(`Calling: ${progress.functionName}`)
73+
console.log(`Args: ${progress.args}`)
74+
// Provide the return value and resume
75+
progress = progress.resume({ returnValue: 10 })
76+
}
77+
// progress is now MontyComplete
78+
console.log(progress.output) // 20
79+
```
80+
81+
## Error Handling
82+
83+
```ts
84+
import { Monty, MontySyntaxError, MontyRuntimeError, MontyTypingError } from '@pydantic/monty'
85+
86+
try {
87+
const m = new Monty('1 / 0')
88+
m.run()
89+
} catch (error) {
90+
if (error instanceof MontySyntaxError) {
91+
console.log('Syntax error:', error.message)
92+
} else if (error instanceof MontyRuntimeError) {
93+
console.log('Runtime error:', error.message)
94+
console.log('Traceback:', error.traceback())
95+
} else if (error instanceof MontyTypingError) {
96+
console.log('Type error:', error.displayDiagnostics())
97+
}
98+
}
99+
```
100+
101+
## Type Checking
102+
103+
```ts
104+
const m = new Monty('"hello" + 1')
105+
try {
106+
m.typeCheck()
107+
} catch (error) {
108+
if (error instanceof MontyTypingError) {
109+
console.log(error.displayDiagnostics('concise'))
110+
}
111+
}
112+
113+
// Or enable during construction
114+
const m2 = new Monty('1 + 1', { typeCheck: true })
115+
```
116+
117+
## Resource Limits
118+
119+
```ts
120+
const m = new Monty('1 + 1')
121+
const result = m.run({
122+
limits: {
123+
maxAllocations: 10000,
124+
maxDurationSecs: 5,
125+
maxMemory: 1024 * 1024, // 1MB
126+
maxRecursionDepth: 100,
127+
},
128+
})
129+
```
130+
131+
## Serialization
132+
133+
```ts
134+
// Save parsed code to avoid re-parsing
135+
const m = new Monty('complex_code()')
136+
const data = m.dump()
137+
138+
// Later, restore without re-parsing
139+
const m2 = Monty.load(data)
140+
const result = m2.run()
141+
142+
// Snapshots can also be serialized
143+
const snapshot = m.start()
144+
if (snapshot instanceof MontySnapshot) {
145+
const snapshotData = snapshot.dump()
146+
// Later, restore and resume
147+
const restored = MontySnapshot.load(snapshotData)
148+
const result = restored.resume({ returnValue: 42 })
149+
}
150+
```
151+
152+
## API Reference
153+
154+
### `Monty` Class
155+
156+
- `constructor(code: string, options?: MontyOptions)` - Parse Python code
157+
- `run(options?: RunOptions)` - Execute and return the result
158+
- `start(options?: StartOptions)` - Start iterative execution
159+
- `typeCheck(prefixCode?: string)` - Perform static type checking
160+
- `dump()` - Serialize to binary format
161+
- `Monty.load(data)` - Deserialize from binary format
162+
- `scriptName` - The script name (default: `'main.py'`)
163+
- `inputs` - Declared input variable names
164+
- `externalFunctions` - Declared external function names
165+
166+
### `MontyOptions`
167+
168+
- `scriptName?: string` - Name used in tracebacks (default: `'main.py'`)
169+
- `inputs?: string[]` - Input variable names
170+
- `externalFunctions?: string[]` - External function names
171+
- `typeCheck?: boolean` - Enable type checking on construction
172+
- `typeCheckPrefixCode?: string` - Code to prepend for type checking
173+
174+
### `RunOptions`
175+
176+
- `inputs?: object` - Input variable values
177+
- `limits?: ResourceLimits` - Resource limits
178+
- `externalFunctions?: object` - External function callbacks
179+
180+
### `ResourceLimits`
181+
182+
- `maxAllocations?: number` - Maximum heap allocations
183+
- `maxDurationSecs?: number` - Maximum execution time in seconds
184+
- `maxMemory?: number` - Maximum heap memory in bytes
185+
- `gcInterval?: number` - Run GC every N allocations
186+
- `maxRecursionDepth?: number` - Maximum call stack depth (default: 1000)
187+
188+
### `MontySnapshot` Class
189+
190+
Returned by `start()` when execution pauses at an external function call.
191+
192+
- `scriptName` - The script being executed
193+
- `functionName` - The external function being called
194+
- `args` - Positional arguments
195+
- `kwargs` - Keyword arguments
196+
- `resume(options: ResumeOptions)` - Resume with return value or exception
197+
- `dump()` / `MontySnapshot.load(data)` - Serialization
198+
199+
### `MontyComplete` Class
200+
201+
Returned by `start()` or `resume()` when execution completes.
202+
203+
- `output` - The final result value
204+
205+
### Error Classes
32206

33-
- `run(code: string): { output: string, result: string }` — execute Python code
34-
in a sandboxed Monty VM. `output` contains captured `print()` output; `result`
35-
is the debug (`{:?}`) representation of the last expression's value.
207+
- `MontyError` - Base class for all Monty errors
208+
- `MontySyntaxError` - Syntax/parsing errors
209+
- `MontyRuntimeError` - Runtime exceptions (with `traceback()`)
210+
- `MontyTypingError` - Type checking errors (with `displayDiagnostics()`)

0 commit comments

Comments
 (0)