@pydantic/monty 0.0.1 → 0.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # @pydantic/monty
2
2
 
3
- JavaScript bindings for the Monty sandboxed Python interpreter.
3
+ JavaScript/TypeScript bindings for the Monty sandboxed Python interpreter.
4
4
 
5
5
  ## Installation
6
6
 
@@ -8,28 +8,203 @@ JavaScript bindings for the Monty sandboxed Python interpreter.
8
8
  npm install @pydantic/monty
9
9
  ```
10
10
 
11
- ## Usage (CommonJS)
11
+ ## Basic Usage
12
12
 
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
+ ```
15
27
 
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
19
40
  ```
20
41
 
21
- ## Usage (ESM / TypeScript)
42
+ For async external functions, use `runMontyAsync()`:
22
43
 
23
44
  ```ts
24
- import monty from '@pydantic/monty'
45
+ import { Monty, runMontyAsync } from '@pydantic/monty'
25
46
 
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
+ })
29
61
  ```
30
62
 
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
32
206
 
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()`)
package/index.d.ts CHANGED
@@ -1,26 +1,311 @@
1
- /* auto-generated by NAPI-RS */
2
- /* eslint-disable */
1
+ // index-header.d.ts - header will be written into index.d.ts on build
2
+
3
+ type JsMontyObject = any
3
4
  /**
4
- * Runs Python code and returns the result as a string.
5
+ * A sandboxed Python interpreter instance.
5
6
  *
6
- * The code is executed in a sandboxed environment with no resource limits.
7
- * Print statements are captured and returned along with the final result.
7
+ * Parses and compiles Python code on initialization, then can be run
8
+ * multiple times with different input values. This separates the parsing
9
+ * cost from execution, making repeated runs more efficient.
10
+ */
11
+ export declare class Monty {
12
+ /**
13
+ * Creates a new Monty interpreter by parsing the given code.
14
+ *
15
+ * Returns either a Monty instance, a MontyException (for syntax errors), or a MontyTypingError.
16
+ * The wrapper should check the result type and throw the appropriate error.
17
+ *
18
+ * @param code - Python code to execute
19
+ * @param options - Configuration options
20
+ * @returns Monty instance on success, or error object on failure
21
+ */
22
+ static create(code: string, options?: MontyOptions | undefined | null): Self | MontyException | MontyTypingError
23
+ /**
24
+ * Performs static type checking on the code.
25
+ *
26
+ * Returns either nothing (success) or a MontyTypingError.
27
+ *
28
+ * @param prefixCode - Optional code to prepend before type checking
29
+ * @returns null on success, or MontyTypingError on failure
30
+ */
31
+ typeCheck(prefixCode?: string | undefined | null): MontyTypingError | null
32
+ /**
33
+ * Executes the code and returns the result, or an exception object if execution fails.
34
+ *
35
+ * @param options - Execution options (inputs, limits, externalFunctions)
36
+ * @returns The result of the last expression, or a MontyException if execution fails
37
+ */
38
+ run(options?: RunOptions | undefined | null): JsMontyObject | MontyException
39
+ /**
40
+ * Starts execution and returns either a snapshot (paused at external call), completion, or error.
41
+ *
42
+ * This method enables iterative execution where code pauses at external function
43
+ * calls, allowing the host to provide return values or exceptions before resuming.
44
+ *
45
+ * @param options - Execution options (inputs, limits)
46
+ * @returns MontySnapshot if paused, MontyComplete if done, or MontyException if failed
47
+ */
48
+ start(options?: StartOptions | undefined | null): MontySnapshot | MontyComplete | MontyException
49
+ /**
50
+ * Serializes the Monty instance to a binary format.
51
+ *
52
+ * The serialized data can be stored and later restored with `Monty.load()`.
53
+ * This allows caching parsed code to avoid re-parsing on subsequent runs.
54
+ *
55
+ * @returns Buffer containing the serialized Monty instance
56
+ */
57
+ dump(): Buffer
58
+ /**
59
+ * Deserializes a Monty instance from binary format.
60
+ *
61
+ * @param data - The serialized Monty data from `dump()`
62
+ * @returns A new Monty instance
63
+ */
64
+ static load(data: Buffer): Monty
65
+ /** Returns the script name. */
66
+ get scriptName(): string
67
+ /** Returns the input variable names. */
68
+ get inputs(): Array<string>
69
+ /** Returns the external function names. */
70
+ get externalFunctions(): Array<string>
71
+ /** Returns a string representation of the Monty instance. */
72
+ repr(): string
73
+ }
74
+
75
+ /**
76
+ * Represents completed execution with a final output value.
8
77
  *
9
- * # Arguments
10
- * * `code` - The Python code to execute
78
+ * The output value is stored as a `MontyObject` internally and converted to JS on access.
79
+ */
80
+ export declare class MontyComplete {
81
+ /** Returns the final output value from the executed code. */
82
+ get output(): JsMontyObject
83
+ /** Returns a string representation of the MontyComplete. */
84
+ repr(): string
85
+ }
86
+
87
+ /**
88
+ * Wrapper around core `MontyException` for napi bindings.
11
89
  *
12
- * # Returns
13
- * A `RunResult` containing the printed output and the result of the last expression.
90
+ * This is a thin newtype wrapper that exposes the necessary getters for the
91
+ * JavaScript wrapper to construct appropriate error types (`MontySyntaxError`
92
+ * or `MontyRuntimeError`) based on the exception type.
93
+ */
94
+ export declare class MontyException {
95
+ /**
96
+ * Returns information about the inner Python exception.
97
+ *
98
+ * The `typeName` field can be used to distinguish syntax errors (`"SyntaxError"`)
99
+ * from runtime errors (e.g., `"ValueError"`, `"TypeError"`).
100
+ */
101
+ get exception(): ExceptionInfo
102
+ /** Returns the error message. */
103
+ get message(): string
104
+ /**
105
+ * Returns the Monty traceback as an array of Frame objects.
106
+ *
107
+ * For syntax errors, this will be an empty array.
108
+ * For runtime errors, this contains the stack frames where the error occurred.
109
+ */
110
+ traceback(): Array<Frame>
111
+ /**
112
+ * Returns formatted exception string.
113
+ *
114
+ * @param format - Output format:
115
+ * - 'traceback' - Full traceback (default)
116
+ * - 'type-msg' - 'ExceptionType: message' format
117
+ * - 'msg' - just the message
118
+ */
119
+ display(format?: string | undefined | null): string
120
+ /** Returns a string representation of the error. */
121
+ toString(): string
122
+ }
123
+ export type JsMontyException = MontyException
124
+
125
+ /**
126
+ * Represents paused execution waiting for an external function call return value.
127
+ *
128
+ * Contains information about the pending external function call and allows
129
+ * resuming execution with the return value or an exception.
130
+ */
131
+ export declare class MontySnapshot {
132
+ /** Returns the name of the script being executed. */
133
+ get scriptName(): string
134
+ /** Returns the name of the external function being called. */
135
+ get functionName(): string
136
+ /** Returns the positional arguments passed to the external function. */
137
+ get args(): Array<JsMontyObject>
138
+ /** Returns the keyword arguments passed to the external function as an object. */
139
+ get kwargs(): object
140
+ /**
141
+ * Resumes execution with either a return value or an exception.
142
+ *
143
+ * Exactly one of `returnValue` or `exception` must be provided.
144
+ *
145
+ * @param options - Object with either `returnValue` or `exception`
146
+ * @returns MontySnapshot if paused, MontyComplete if done, or MontyException if failed
147
+ */
148
+ resume(options: ResumeOptions): Self | MontyComplete | MontyException
149
+ /**
150
+ * Serializes the MontySnapshot to a binary format.
151
+ *
152
+ * The serialized data can be stored and later restored with `MontySnapshot.load()`.
153
+ * This allows suspending execution and resuming later, potentially in a different process.
154
+ *
155
+ * @returns Buffer containing the serialized snapshot
156
+ */
157
+ dump(): Buffer
158
+ /**
159
+ * Deserializes a MontySnapshot from binary format.
160
+ *
161
+ * @param data - The serialized snapshot data from `dump()`
162
+ * @param options - Optional load options (reserved for future use)
163
+ * @returns A new MontySnapshot instance
164
+ */
165
+ static load(data: Buffer, options?: SnapshotLoadOptions | undefined | null): MontySnapshot
166
+ /** Returns a string representation of the MontySnapshot. */
167
+ repr(): string
168
+ }
169
+
170
+ /**
171
+ * Raised when type checking finds errors in the code.
172
+ *
173
+ * This exception is raised when static type analysis detects type errors.
174
+ * Use `display()` to render diagnostics in various formats.
175
+ */
176
+ export declare class MontyTypingError {
177
+ /** Returns information about the inner exception. */
178
+ get exception(): ExceptionInfo
179
+ /** Returns the error message. */
180
+ get message(): string
181
+ /**
182
+ * Renders the type error diagnostics with the specified format and color.
183
+ *
184
+ * @param format - Output format. One of:
185
+ * - 'full' - Full diagnostic output (default)
186
+ * - 'concise' - Concise output
187
+ * - 'azure' - Azure DevOps format
188
+ * - 'json' - JSON format
189
+ * - 'jsonlines' - JSON Lines format
190
+ * - 'rdjson' - RDJson format
191
+ * - 'pylint' - Pylint format
192
+ * - 'gitlab' - GitLab CI format
193
+ * - 'github' - GitHub Actions format
194
+ * @param color - Whether to include ANSI color codes. Default: false
195
+ */
196
+ display(format?: string | undefined | null, color?: boolean | undefined | null): string
197
+ /** Returns a string representation of the error. */
198
+ toString(): string
199
+ }
200
+
201
+ /**
202
+ * Information about the inner Python exception.
14
203
  *
15
- * # Errors
16
- * Returns an error if the code fails to parse or encounters a runtime error.
204
+ * This provides structured access to the exception type and message
205
+ * for programmatic error handling.
17
206
  */
18
- export declare function run(code: string): RunResult
207
+ export interface ExceptionInfo {
208
+ /** The exception type name (e.g., "ValueError", "TypeError", "SyntaxError"). */
209
+ typeName: string
210
+ /** The exception message. */
211
+ message: string
212
+ }
213
+
214
+ /** Input for raising an exception during resume. */
215
+ export interface ExceptionInput {
216
+ /** The exception type name (e.g., "ValueError"). */
217
+ type: string
218
+ /** The exception message. */
219
+ message: string
220
+ }
221
+
222
+ /**
223
+ * A single frame in a Monty traceback.
224
+ *
225
+ * Contains all the information needed to display a traceback line:
226
+ * the file location, function name, and optional source code preview.
227
+ */
228
+ export interface Frame {
229
+ /** The filename where the code is located. */
230
+ filename: string
231
+ /** Line number (1-based). */
232
+ line: number
233
+ /** Column number (1-based). */
234
+ column: number
235
+ /** End line number (1-based). */
236
+ endLine: number
237
+ /** End column number (1-based). */
238
+ endColumn: number
239
+ /** The name of the function, or null for module-level code. */
240
+ functionName?: string
241
+ /** The source code line for preview in the traceback. */
242
+ sourceLine?: string
243
+ }
244
+
245
+ /** Options for creating a new Monty instance. */
246
+ export interface MontyOptions {
247
+ /** Name used in tracebacks and error messages. Default: 'main.py' */
248
+ scriptName?: string
249
+ /** List of input variable names available in the code. */
250
+ inputs?: Array<string>
251
+ /** List of external function names the code can call. */
252
+ externalFunctions?: Array<string>
253
+ /** Whether to perform type checking on the code. Default: false */
254
+ typeCheck?: boolean
255
+ /** Optional code to prepend before type checking. */
256
+ typeCheckPrefixCode?: string
257
+ }
258
+
259
+ /**
260
+ * Resource limits configuration from JavaScript.
261
+ *
262
+ * All limits are optional. Omit a key to disable that limit.
263
+ */
264
+ export interface ResourceLimits {
265
+ /** Maximum number of heap allocations allowed. */
266
+ maxAllocations?: number
267
+ /** Maximum execution time in seconds. */
268
+ maxDurationSecs?: number
269
+ /** Maximum heap memory in bytes. */
270
+ maxMemory?: number
271
+ /** Run garbage collection every N allocations. */
272
+ gcInterval?: number
273
+ /** Maximum function call stack depth (default: 1000). */
274
+ maxRecursionDepth?: number
275
+ }
276
+
277
+ /** Options for resuming execution. */
278
+ export interface ResumeOptions {
279
+ /** The value to return from the external function call. */
280
+ returnValue?: unknown
281
+ /**
282
+ * An exception to raise in the interpreter.
283
+ * Format: { type: string, message: string }
284
+ */
285
+ exception?: ExceptionInput
286
+ }
287
+
288
+ /** Options for running code. */
289
+ export interface RunOptions {
290
+ inputs?: object
291
+ /** Resource limits configuration. */
292
+ limits?: ResourceLimits
293
+ /**
294
+ * Dict of external function callbacks.
295
+ * Keys are function names, values are callable functions.
296
+ */
297
+ externalFunctions?: object
298
+ }
299
+
300
+ /** Options for loading a serialized snapshot. */
301
+ export interface SnapshotLoadOptions {
302
+
303
+ }
19
304
 
20
- /** Result of running Python code. */
21
- export interface RunResult {
22
- /** Any output from print statements during execution. */
23
- output: string
24
- /** The debug representation of the final result. */
25
- result: string
305
+ /** Options for starting execution. */
306
+ export interface StartOptions {
307
+ /** Dict of input variable values. */
308
+ inputs?: object
309
+ /** Resource limits configuration. */
310
+ limits?: ResourceLimits
26
311
  }