@pydantic/monty 0.0.1

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/wrapper.js ADDED
@@ -0,0 +1,423 @@
1
+ // Custom error classes that extend Error for proper JavaScript error handling.
2
+ // These wrap the native Rust classes to provide instanceof support.
3
+ import { Monty as NativeMonty, MontySnapshot as NativeMontySnapshot, MontyComplete as NativeMontyComplete, MontyException as NativeMontyException, MontyTypingError as NativeMontyTypingError, } from './index.js';
4
+ /**
5
+ * Base class for all Monty interpreter errors.
6
+ *
7
+ * This is the parent class for `MontySyntaxError`, `MontyRuntimeError`, and `MontyTypingError`.
8
+ * Catching `MontyError` will catch any exception raised by Monty.
9
+ */
10
+ export class MontyError extends Error {
11
+ constructor(typeName, message) {
12
+ super(message ? `${typeName}: ${message}` : typeName);
13
+ this.name = 'MontyError';
14
+ this._typeName = typeName;
15
+ this._message = message;
16
+ // Maintains proper stack trace for where our error was thrown (only available on V8)
17
+ if (Error.captureStackTrace) {
18
+ Error.captureStackTrace(this, MontyError);
19
+ }
20
+ }
21
+ /**
22
+ * Returns information about the inner Python exception.
23
+ */
24
+ get exception() {
25
+ return {
26
+ typeName: this._typeName,
27
+ message: this._message,
28
+ };
29
+ }
30
+ /**
31
+ * Returns formatted exception string.
32
+ * @param format - 'type-msg' for 'ExceptionType: message', 'msg' for just the message
33
+ */
34
+ display(format = 'msg') {
35
+ switch (format) {
36
+ case 'msg':
37
+ return this._message;
38
+ case 'type-msg':
39
+ return this._message ? `${this._typeName}: ${this._message}` : this._typeName;
40
+ default:
41
+ throw new Error(`Invalid display format: '${format}'. Expected 'type-msg' or 'msg'`);
42
+ }
43
+ }
44
+ }
45
+ /**
46
+ * Raised when Python code has syntax errors or cannot be parsed by Monty.
47
+ *
48
+ * The inner exception is always a `SyntaxError`. Use `display()` to get
49
+ * formatted error output.
50
+ */
51
+ export class MontySyntaxError extends MontyError {
52
+ constructor(messageOrNative) {
53
+ if (typeof messageOrNative === 'string') {
54
+ super('SyntaxError', messageOrNative);
55
+ this._native = null;
56
+ }
57
+ else {
58
+ const exc = messageOrNative.exception;
59
+ super('SyntaxError', exc.message);
60
+ this._native = messageOrNative;
61
+ }
62
+ this.name = 'MontySyntaxError';
63
+ if (Error.captureStackTrace) {
64
+ Error.captureStackTrace(this, MontySyntaxError);
65
+ }
66
+ }
67
+ /**
68
+ * Returns formatted exception string.
69
+ * @param format - 'type-msg' for 'SyntaxError: message', 'msg' for just the message
70
+ */
71
+ display(format = 'msg') {
72
+ if (this._native && typeof this._native.display === 'function') {
73
+ return this._native.display(format);
74
+ }
75
+ return super.display(format);
76
+ }
77
+ }
78
+ /**
79
+ * Raised when Monty code fails during execution.
80
+ *
81
+ * Provides access to the traceback frames where the error occurred via `traceback()`,
82
+ * and formatted output via `display()`.
83
+ */
84
+ export class MontyRuntimeError extends MontyError {
85
+ constructor(nativeOrTypeName, message, tracebackString, frames) {
86
+ if (typeof nativeOrTypeName === 'string') {
87
+ // Legacy constructor: (typeName, message, tracebackString, frames)
88
+ super(nativeOrTypeName, message);
89
+ this._native = null;
90
+ this._tracebackString = tracebackString ?? null;
91
+ this._frames = frames ?? null;
92
+ }
93
+ else {
94
+ // New constructor: (nativeException)
95
+ const exc = nativeOrTypeName.exception;
96
+ super(exc.typeName, exc.message);
97
+ this._native = nativeOrTypeName;
98
+ this._tracebackString = null;
99
+ this._frames = null;
100
+ }
101
+ this.name = 'MontyRuntimeError';
102
+ if (Error.captureStackTrace) {
103
+ Error.captureStackTrace(this, MontyRuntimeError);
104
+ }
105
+ }
106
+ /**
107
+ * Returns the Monty traceback as an array of Frame objects.
108
+ */
109
+ traceback() {
110
+ if (this._native) {
111
+ return this._native.traceback();
112
+ }
113
+ return this._frames || [];
114
+ }
115
+ /**
116
+ * Returns formatted exception string.
117
+ * @param format - 'traceback' for full traceback, 'type-msg' for 'ExceptionType: message', 'msg' for just the message
118
+ */
119
+ display(format = 'traceback') {
120
+ if (this._native && typeof this._native.display === 'function') {
121
+ return this._native.display(format);
122
+ }
123
+ // Fallback for legacy constructor
124
+ switch (format) {
125
+ case 'traceback':
126
+ return this._tracebackString || this.message;
127
+ case 'type-msg':
128
+ return this._message ? `${this._typeName}: ${this._message}` : this._typeName;
129
+ case 'msg':
130
+ return this._message;
131
+ default:
132
+ throw new Error(`Invalid display format: '${format}'. Expected 'traceback', 'type-msg', or 'msg'`);
133
+ }
134
+ }
135
+ }
136
+ /**
137
+ * Raised when type checking finds errors in the code.
138
+ *
139
+ * This exception is raised when static type analysis detects type errors.
140
+ * Use `displayDiagnostics()` to render rich diagnostics in various formats for tooling integration.
141
+ * Use `display()` (inherited) for simple 'type-msg' or 'msg' formats.
142
+ */
143
+ export class MontyTypingError extends MontyError {
144
+ constructor(messageOrNative, nativeError = null) {
145
+ if (typeof messageOrNative === 'string') {
146
+ super('TypeError', messageOrNative);
147
+ this._native = nativeError;
148
+ }
149
+ else {
150
+ const exc = messageOrNative.exception;
151
+ super('TypeError', exc.message);
152
+ this._native = messageOrNative;
153
+ }
154
+ this.name = 'MontyTypingError';
155
+ if (Error.captureStackTrace) {
156
+ Error.captureStackTrace(this, MontyTypingError);
157
+ }
158
+ }
159
+ /**
160
+ * Renders rich type error diagnostics for tooling integration.
161
+ *
162
+ * @param format - Output format (default: 'full')
163
+ * @param color - Include ANSI color codes (default: false)
164
+ */
165
+ displayDiagnostics(format = 'full', color = false) {
166
+ if (this._native && typeof this._native.display === 'function') {
167
+ return this._native.display(format, color);
168
+ }
169
+ return this._message;
170
+ }
171
+ }
172
+ /**
173
+ * Wrapped Monty class that throws proper Error subclasses.
174
+ */
175
+ export class Monty {
176
+ /**
177
+ * Creates a new Monty interpreter by parsing the given code.
178
+ *
179
+ * @param code - Python code to execute
180
+ * @param options - Configuration options
181
+ * @throws {MontySyntaxError} If the code has syntax errors
182
+ * @throws {MontyTypingError} If type checking is enabled and finds errors
183
+ */
184
+ constructor(code, options) {
185
+ const result = NativeMonty.create(code, options);
186
+ if (result instanceof NativeMontyException) {
187
+ // Check typeName to distinguish syntax errors from other exceptions
188
+ if (result.exception.typeName === 'SyntaxError') {
189
+ throw new MontySyntaxError(result);
190
+ }
191
+ throw new MontyRuntimeError(result);
192
+ }
193
+ if (result instanceof NativeMontyTypingError) {
194
+ throw new MontyTypingError(result);
195
+ }
196
+ this._native = result;
197
+ }
198
+ /**
199
+ * Performs static type checking on the code.
200
+ *
201
+ * @param prefixCode - Optional code to prepend before type checking
202
+ * @throws {MontyTypingError} If type checking finds errors
203
+ */
204
+ typeCheck(prefixCode) {
205
+ const result = this._native.typeCheck(prefixCode);
206
+ if (result instanceof NativeMontyTypingError) {
207
+ throw new MontyTypingError(result);
208
+ }
209
+ }
210
+ /**
211
+ * Executes the code and returns the result.
212
+ *
213
+ * @param options - Execution options (inputs, limits)
214
+ * @returns The result of the last expression
215
+ * @throws {MontyRuntimeError} If the code raises an exception
216
+ */
217
+ run(options) {
218
+ const result = this._native.run(options);
219
+ if (result instanceof NativeMontyException) {
220
+ throw new MontyRuntimeError(result);
221
+ }
222
+ return result;
223
+ }
224
+ /**
225
+ * Starts execution and returns either a snapshot (paused at external call) or completion.
226
+ *
227
+ * @param options - Execution options (inputs, limits)
228
+ * @returns MontySnapshot if an external function call is pending, MontyComplete if done
229
+ * @throws {MontyRuntimeError} If the code raises an exception
230
+ */
231
+ start(options) {
232
+ const result = this._native.start(options);
233
+ return wrapStartResult(result);
234
+ }
235
+ /**
236
+ * Serializes the Monty instance to a binary format.
237
+ */
238
+ dump() {
239
+ return this._native.dump();
240
+ }
241
+ /**
242
+ * Deserializes a Monty instance from binary format.
243
+ */
244
+ static load(data) {
245
+ const instance = Object.create(Monty.prototype);
246
+ instance._native = NativeMonty.load(data);
247
+ return instance;
248
+ }
249
+ /** Returns the script name. */
250
+ get scriptName() {
251
+ return this._native.scriptName;
252
+ }
253
+ /** Returns the input variable names. */
254
+ get inputs() {
255
+ return this._native.inputs;
256
+ }
257
+ /** Returns the external function names. */
258
+ get externalFunctions() {
259
+ return this._native.externalFunctions;
260
+ }
261
+ /** Returns a string representation of the Monty instance. */
262
+ repr() {
263
+ return this._native.repr();
264
+ }
265
+ }
266
+ /**
267
+ * Helper to wrap native start/resume results, throwing errors as needed.
268
+ */
269
+ function wrapStartResult(result) {
270
+ if (result instanceof NativeMontyException) {
271
+ throw new MontyRuntimeError(result);
272
+ }
273
+ if (result instanceof NativeMontySnapshot) {
274
+ return new MontySnapshot(result);
275
+ }
276
+ if (result instanceof NativeMontyComplete) {
277
+ return new MontyComplete(result);
278
+ }
279
+ throw new Error(`Unexpected result type from native binding: ${result}`);
280
+ }
281
+ /**
282
+ * Represents paused execution waiting for an external function call return value.
283
+ *
284
+ * Contains information about the pending external function call and allows
285
+ * resuming execution with the return value or an exception.
286
+ */
287
+ export class MontySnapshot {
288
+ constructor(nativeSnapshot) {
289
+ this._native = nativeSnapshot;
290
+ }
291
+ /** Returns the name of the script being executed. */
292
+ get scriptName() {
293
+ return this._native.scriptName;
294
+ }
295
+ /** Returns the name of the external function being called. */
296
+ get functionName() {
297
+ return this._native.functionName;
298
+ }
299
+ /** Returns the positional arguments passed to the external function. */
300
+ get args() {
301
+ return this._native.args;
302
+ }
303
+ /** Returns the keyword arguments passed to the external function as an object. */
304
+ get kwargs() {
305
+ return this._native.kwargs;
306
+ }
307
+ /**
308
+ * Resumes execution with either a return value or an exception.
309
+ *
310
+ * @param options - Object with either `returnValue` or `exception`
311
+ * @returns MontySnapshot if another external call is pending, MontyComplete if done
312
+ * @throws {MontyRuntimeError} If the code raises an exception
313
+ */
314
+ resume(options) {
315
+ const result = this._native.resume(options);
316
+ return wrapStartResult(result);
317
+ }
318
+ /**
319
+ * Serializes the MontySnapshot to a binary format.
320
+ */
321
+ dump() {
322
+ return this._native.dump();
323
+ }
324
+ /**
325
+ * Deserializes a MontySnapshot from binary format.
326
+ */
327
+ static load(data, options) {
328
+ const nativeSnapshot = NativeMontySnapshot.load(data, options);
329
+ return new MontySnapshot(nativeSnapshot);
330
+ }
331
+ /** Returns a string representation of the MontySnapshot. */
332
+ repr() {
333
+ return this._native.repr();
334
+ }
335
+ }
336
+ /**
337
+ * Represents completed execution with a final output value.
338
+ */
339
+ export class MontyComplete {
340
+ constructor(nativeComplete) {
341
+ this._native = nativeComplete;
342
+ }
343
+ /** Returns the final output value from the executed code. */
344
+ get output() {
345
+ return this._native.output;
346
+ }
347
+ /** Returns a string representation of the MontyComplete. */
348
+ repr() {
349
+ return this._native.repr();
350
+ }
351
+ }
352
+ /**
353
+ * Runs a Monty script with async external function support.
354
+ *
355
+ * This function handles both synchronous and asynchronous external functions.
356
+ * When an external function returns a Promise, it will be awaited before
357
+ * resuming execution.
358
+ *
359
+ * @param montyRunner - The Monty runner instance to execute
360
+ * @param options - Execution options
361
+ * @returns The output of the Monty script
362
+ * @throws {MontyRuntimeError} If the code raises an exception
363
+ * @throws {MontySyntaxError} If the code has syntax errors
364
+ *
365
+ * @example
366
+ * const m = new Monty('result = await fetch_data(url)', {
367
+ * inputs: ['url'],
368
+ * externalFunctions: ['fetch_data']
369
+ * });
370
+ *
371
+ * const result = await runMontyAsync(m, {
372
+ * inputs: { url: 'https://example.com' },
373
+ * externalFunctions: {
374
+ * fetch_data: async (url) => {
375
+ * const response = await fetch(url);
376
+ * return response.text();
377
+ * }
378
+ * }
379
+ * });
380
+ */
381
+ export async function runMontyAsync(montyRunner, options = {}) {
382
+ const { inputs, externalFunctions = {}, limits } = options;
383
+ let progress = montyRunner.start({ inputs, limits });
384
+ while (progress instanceof MontySnapshot) {
385
+ const snapshot = progress;
386
+ const funcName = snapshot.functionName;
387
+ const extFunction = externalFunctions[funcName];
388
+ if (!extFunction) {
389
+ // Function not found - resume with a KeyError exception
390
+ progress = snapshot.resume({
391
+ exception: {
392
+ type: 'KeyError',
393
+ message: `"External function '${funcName}' not found"`,
394
+ },
395
+ });
396
+ continue;
397
+ }
398
+ try {
399
+ // Call the external function
400
+ let result = extFunction(...snapshot.args, snapshot.kwargs);
401
+ // If the result is a Promise, await it
402
+ if (result && typeof result.then === 'function') {
403
+ result = await result;
404
+ }
405
+ // Resume with the return value
406
+ progress = snapshot.resume({ returnValue: result });
407
+ }
408
+ catch (error) {
409
+ // External function threw an exception - convert to Monty exception
410
+ const err = error;
411
+ const excType = err.name || 'RuntimeError';
412
+ const excMessage = err.message || String(error);
413
+ progress = snapshot.resume({
414
+ exception: {
415
+ type: excType,
416
+ message: excMessage,
417
+ },
418
+ });
419
+ }
420
+ }
421
+ return progress.output;
422
+ }
423
+ //# sourceMappingURL=wrapper.js.map