pyodide 0.20.0-alpha.1 → 0.20.0
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/api.ts +73 -37
- package/error_handling.gen.ts +51 -10
- package/index.test-d.ts +5 -0
- package/load-package.ts +18 -6
- package/package.json +1 -1
- package/pyproxy.gen.ts +90 -1
package/api.ts
CHANGED
|
@@ -33,22 +33,22 @@ export let version: string = ""; // actually defined in loadPyodide (see pyodide
|
|
|
33
33
|
|
|
34
34
|
let runPythonPositionalGlobalsDeprecationWarned = false;
|
|
35
35
|
/**
|
|
36
|
-
* Runs a string of Python code from JavaScript.
|
|
36
|
+
* Runs a string of Python code from JavaScript, using :any:`pyodide.eval_code`
|
|
37
|
+
* to evaluate the code. If the last statement in the Python code is an
|
|
38
|
+
* expression (and the code doesn't end with a semicolon), the value of the
|
|
39
|
+
* expression is returned.
|
|
37
40
|
*
|
|
38
|
-
*
|
|
39
|
-
*
|
|
41
|
+
* .. admonition:: Positional globals argument
|
|
42
|
+
* :class: warning
|
|
40
43
|
*
|
|
41
|
-
*
|
|
42
|
-
*
|
|
43
|
-
*
|
|
44
|
-
* positional argument rather than as a named argument. In v0.20 this will
|
|
45
|
-
* still work but it is deprecated. It will be removed in v0.21.
|
|
44
|
+
* In Pyodide v0.19, this function took the globals parameter as a positional
|
|
45
|
+
* argument rather than as a named argument. In v0.20 this will still work
|
|
46
|
+
* but it is deprecated. It will be removed in v0.21.
|
|
46
47
|
*
|
|
47
48
|
* @param code Python code to evaluate
|
|
48
49
|
* @param options
|
|
49
50
|
* @param options.globals An optional Python dictionary to use as the globals.
|
|
50
|
-
* Defaults to :any:`pyodide.globals`.
|
|
51
|
-
* :any:`pyodide.eval_code` to evaluate the code.
|
|
51
|
+
* Defaults to :any:`pyodide.globals`.
|
|
52
52
|
* @returns The result of the Python code translated to JavaScript. See the
|
|
53
53
|
* documentation for :any:`pyodide.eval_code` for more info.
|
|
54
54
|
*/
|
|
@@ -60,7 +60,7 @@ export function runPython(
|
|
|
60
60
|
options = { globals: options as PyProxy };
|
|
61
61
|
if (!runPythonPositionalGlobalsDeprecationWarned) {
|
|
62
62
|
console.warn(
|
|
63
|
-
"Passing a PyProxy as the second argument to runPython is deprecated. Use 'runPython(code, {globals : some_dict})' instead."
|
|
63
|
+
"Passing a PyProxy as the second argument to runPython is deprecated and will be removed in v0.21. Use 'runPython(code, {globals : some_dict})' instead."
|
|
64
64
|
);
|
|
65
65
|
runPythonPositionalGlobalsDeprecationWarned = true;
|
|
66
66
|
}
|
|
@@ -122,8 +122,11 @@ export async function loadPackagesFromImports(
|
|
|
122
122
|
}
|
|
123
123
|
|
|
124
124
|
/**
|
|
125
|
-
*
|
|
126
|
-
*
|
|
125
|
+
* Run a Python code string with top level await using
|
|
126
|
+
* :any:`pyodide.eval_code_async` to evaluate the code. Returns a promise which
|
|
127
|
+
* resolves when execution completes. If the last statement in the Python code
|
|
128
|
+
* is an expression (and the code doesn't end with a semicolon), the returned
|
|
129
|
+
* promise will resolve to the value of this expression.
|
|
127
130
|
*
|
|
128
131
|
* For example:
|
|
129
132
|
*
|
|
@@ -138,13 +141,15 @@ export async function loadPackagesFromImports(
|
|
|
138
141
|
* `);
|
|
139
142
|
* console.log(result); // 79
|
|
140
143
|
*
|
|
141
|
-
* .. admonition:: Python imports
|
|
144
|
+
* .. admonition:: Python imports
|
|
145
|
+
* :class: warning
|
|
142
146
|
*
|
|
143
147
|
* Since pyodide 0.18.0, you must call :js:func:`loadPackagesFromImports` to
|
|
144
148
|
* import any python packages referenced via `import` statements in your
|
|
145
149
|
* code. This function will no longer do it for you.
|
|
146
150
|
*
|
|
147
|
-
* .. admonition:: Positional globals argument
|
|
151
|
+
* .. admonition:: Positional globals argument
|
|
152
|
+
* :class: warning
|
|
148
153
|
*
|
|
149
154
|
* In Pyodide v0.19, this function took the globals parameter as a
|
|
150
155
|
* positional argument rather than as a named argument. In v0.20 this will
|
|
@@ -153,8 +158,7 @@ export async function loadPackagesFromImports(
|
|
|
153
158
|
* @param code Python code to evaluate
|
|
154
159
|
* @param options
|
|
155
160
|
* @param options.globals An optional Python dictionary to use as the globals.
|
|
156
|
-
* Defaults to :any:`pyodide.globals`.
|
|
157
|
-
* :any:`pyodide.eval_code_async` to evaluate the code.
|
|
161
|
+
* Defaults to :any:`pyodide.globals`.
|
|
158
162
|
* @returns The result of the Python code translated to JavaScript.
|
|
159
163
|
* @async
|
|
160
164
|
*/
|
|
@@ -166,7 +170,7 @@ export async function runPythonAsync(
|
|
|
166
170
|
options = { globals: options as PyProxy };
|
|
167
171
|
if (!runPythonPositionalGlobalsDeprecationWarned) {
|
|
168
172
|
console.warn(
|
|
169
|
-
"Passing a PyProxy as the second argument to
|
|
173
|
+
"Passing a PyProxy as the second argument to runPythonAsync is deprecated and will be removed in v0.21. Use 'runPythonAsync(code, {globals : some_dict})' instead."
|
|
170
174
|
);
|
|
171
175
|
runPythonPositionalGlobalsDeprecationWarned = true;
|
|
172
176
|
}
|
|
@@ -322,25 +326,46 @@ export function pyimport(mod_name: string): PyProxy {
|
|
|
322
326
|
return API.importlib.import_module(mod_name);
|
|
323
327
|
}
|
|
324
328
|
|
|
329
|
+
let unpackArchivePositionalExtractDirDeprecationWarned = false;
|
|
325
330
|
/**
|
|
326
331
|
* Unpack an archive into a target directory.
|
|
327
332
|
*
|
|
333
|
+
* .. admonition:: Positional globals argument :class: warning
|
|
334
|
+
*
|
|
335
|
+
* In Pyodide v0.19, this function took the extract_dir parameter as a
|
|
336
|
+
* positional argument rather than as a named argument. In v0.20 this will
|
|
337
|
+
* still work but it is deprecated. It will be removed in v0.21.
|
|
338
|
+
*
|
|
328
339
|
* @param buffer The archive as an ArrayBuffer or TypedArray.
|
|
329
|
-
* @param format The format of the archive. Should be one of the formats
|
|
330
|
-
* By default the options are 'bztar',
|
|
331
|
-
*
|
|
340
|
+
* @param format The format of the archive. Should be one of the formats
|
|
341
|
+
* recognized by `shutil.unpack_archive`. By default the options are 'bztar',
|
|
342
|
+
* 'gztar', 'tar', 'zip', and 'wheel'. Several synonyms are accepted for each
|
|
343
|
+
* format, e.g., for 'gztar' any of '.gztar', '.tar.gz', '.tgz', 'tar.gz' or
|
|
344
|
+
* 'tgz' are considered to be synonyms.
|
|
332
345
|
*
|
|
333
|
-
* @param
|
|
346
|
+
* @param options
|
|
347
|
+
* @param options.extractDir The directory to unpack the archive into. Defaults
|
|
348
|
+
* to the working directory.
|
|
334
349
|
*/
|
|
335
350
|
export function unpackArchive(
|
|
336
351
|
buffer: TypedArray,
|
|
337
352
|
format: string,
|
|
338
|
-
|
|
353
|
+
options: {
|
|
354
|
+
extractDir?: string;
|
|
355
|
+
} = {}
|
|
339
356
|
) {
|
|
340
|
-
if (
|
|
341
|
-
|
|
357
|
+
if (typeof options === "string") {
|
|
358
|
+
if (!unpackArchivePositionalExtractDirDeprecationWarned) {
|
|
359
|
+
console.warn(
|
|
360
|
+
"Passing a string as the third argument to unpackArchive is deprecated and will be removed in v0.21. Instead use { extract_dir : 'some_path' }"
|
|
361
|
+
);
|
|
362
|
+
unpackArchivePositionalExtractDirDeprecationWarned = true;
|
|
363
|
+
}
|
|
364
|
+
options = { extractDir: options };
|
|
342
365
|
}
|
|
343
|
-
|
|
366
|
+
let extract_dir = options.extractDir;
|
|
367
|
+
API.package_loader.unpack_buffer.callKwargs({
|
|
368
|
+
buffer,
|
|
344
369
|
format,
|
|
345
370
|
extract_dir,
|
|
346
371
|
});
|
|
@@ -357,15 +382,28 @@ API.saveState = () => API.pyodide_py._state.save_state();
|
|
|
357
382
|
API.restoreState = (state: any) => API.pyodide_py._state.restore_state(state);
|
|
358
383
|
|
|
359
384
|
/**
|
|
360
|
-
* Sets the interrupt buffer to be
|
|
361
|
-
* Pyodide is used in a webworker. The buffer should be a
|
|
362
|
-
* shared with the main browser thread (or another
|
|
363
|
-
*
|
|
364
|
-
*
|
|
385
|
+
* Sets the interrupt buffer to be ``interrupt_buffer``. This is only useful
|
|
386
|
+
* when Pyodide is used in a webworker. The buffer should be a
|
|
387
|
+
* ``SharedArrayBuffer`` shared with the main browser thread (or another
|
|
388
|
+
* worker). In that case, signal ``signum`` may be sent by writing ``signum``
|
|
389
|
+
* into the interrupt buffer. If ``signum`` does not satisfy 0 < ``signum`` <
|
|
390
|
+
* ``NSIG`` it will be silently ignored. NSIG is 65 (internally signals are
|
|
391
|
+
* indicated by a bitflag).
|
|
392
|
+
*
|
|
393
|
+
* You can disable interrupts by calling `setInterruptBuffer(undefined)`.
|
|
394
|
+
*
|
|
395
|
+
* If you wish to trigger a ``KeyboardInterrupt``, write ``SIGINT`` (a 2), into
|
|
396
|
+
* the interrupt buffer.
|
|
397
|
+
*
|
|
398
|
+
* By default ``SIGINT`` raises a ``KeyboardInterrupt`` and all other signals
|
|
399
|
+
* are ignored. You can install custom signal handlers with the signal module.
|
|
400
|
+
* Even signals that normally have special meaning and can't be overridden like
|
|
401
|
+
* ``SIGKILL`` and ``SIGSEGV`` are ignored by default and can be used for any
|
|
402
|
+
* purpose you like.
|
|
365
403
|
*/
|
|
366
404
|
export function setInterruptBuffer(interrupt_buffer: TypedArray) {
|
|
367
|
-
|
|
368
|
-
Module.
|
|
405
|
+
Module.HEAP8[Module._Py_EMSCRIPTEN_SIGNAL_HANDLING] = !!interrupt_buffer;
|
|
406
|
+
Module.Py_EmscriptenSignalBuffer = interrupt_buffer;
|
|
369
407
|
}
|
|
370
408
|
|
|
371
409
|
/**
|
|
@@ -377,10 +415,8 @@ export function setInterruptBuffer(interrupt_buffer: TypedArray) {
|
|
|
377
415
|
* during execution of C code.
|
|
378
416
|
*/
|
|
379
417
|
export function checkInterrupt() {
|
|
380
|
-
if (
|
|
381
|
-
|
|
382
|
-
Module._PyErr_SetInterrupt();
|
|
383
|
-
API.runPython("");
|
|
418
|
+
if (Module.__PyErr_CheckSignals()) {
|
|
419
|
+
Module._pythonexc2js();
|
|
384
420
|
}
|
|
385
421
|
}
|
|
386
422
|
|
package/error_handling.gen.ts
CHANGED
|
@@ -11,6 +11,38 @@ API.dump_traceback = function () {
|
|
|
11
11
|
Module.__Py_DumpTraceback(fd_stdout, Module._PyGILState_GetThisThreadState());
|
|
12
12
|
};
|
|
13
13
|
|
|
14
|
+
function ensureCaughtObjectIsError(e: any) {
|
|
15
|
+
if (typeof e === "string") {
|
|
16
|
+
// Sometimes emscripten throws a raw string...
|
|
17
|
+
e = new Error(e);
|
|
18
|
+
} else if (
|
|
19
|
+
typeof e !== "object" ||
|
|
20
|
+
e === null ||
|
|
21
|
+
typeof e.stack !== "string" ||
|
|
22
|
+
typeof e.message !== "string"
|
|
23
|
+
) {
|
|
24
|
+
// We caught something really weird. Be brave!
|
|
25
|
+
let msg = `A value of type ${typeof e} with tag ${Object.prototype.toString.call(
|
|
26
|
+
e
|
|
27
|
+
)} was thrown as an error!`;
|
|
28
|
+
try {
|
|
29
|
+
msg += `\nString interpolation of the thrown value gives """${e}""".`;
|
|
30
|
+
} catch (e) {
|
|
31
|
+
msg += `\nString interpolation of the thrown value fails.`;
|
|
32
|
+
}
|
|
33
|
+
try {
|
|
34
|
+
msg += `\nThe thrown value's toString method returns """${e.toString()}""".`;
|
|
35
|
+
} catch (e) {
|
|
36
|
+
msg += `\nThe thrown value's toString method fails.`;
|
|
37
|
+
}
|
|
38
|
+
e = new Error(msg);
|
|
39
|
+
}
|
|
40
|
+
// Post conditions:
|
|
41
|
+
// 1. typeof e is object
|
|
42
|
+
// 2. hiwire_is_error(e) returns true
|
|
43
|
+
return e;
|
|
44
|
+
}
|
|
45
|
+
|
|
14
46
|
let fatal_error_occurred = false;
|
|
15
47
|
/**
|
|
16
48
|
* Signal a fatal error.
|
|
@@ -25,7 +57,7 @@ let fatal_error_occurred = false;
|
|
|
25
57
|
* @private
|
|
26
58
|
*/
|
|
27
59
|
API.fatal_error = function (e: any) {
|
|
28
|
-
if (e.pyodide_fatal_error) {
|
|
60
|
+
if (e && e.pyodide_fatal_error) {
|
|
29
61
|
return;
|
|
30
62
|
}
|
|
31
63
|
if (fatal_error_occurred) {
|
|
@@ -34,14 +66,10 @@ API.fatal_error = function (e: any) {
|
|
|
34
66
|
return;
|
|
35
67
|
}
|
|
36
68
|
if (typeof e === "number") {
|
|
37
|
-
//
|
|
69
|
+
// Hopefully a C++ exception? Have to do some conversion work.
|
|
38
70
|
e = convertCppException(e);
|
|
39
|
-
} else
|
|
40
|
-
e =
|
|
41
|
-
} else if (typeof e !== "object") {
|
|
42
|
-
e = new Error(
|
|
43
|
-
`An object of type ${typeof e} was thrown as an error. toString returns ${e.toString()}`
|
|
44
|
-
);
|
|
71
|
+
} else {
|
|
72
|
+
e = ensureCaughtObjectIsError(e);
|
|
45
73
|
}
|
|
46
74
|
// Mark e so we know not to handle it later in EM_JS wrappers
|
|
47
75
|
e.pyodide_fatal_error = true;
|
|
@@ -193,7 +221,7 @@ function isErrorStart(frame: ErrorStackParser.StackFrame): boolean {
|
|
|
193
221
|
}
|
|
194
222
|
|
|
195
223
|
Module.handle_js_error = function (e: any) {
|
|
196
|
-
if (e.pyodide_fatal_error) {
|
|
224
|
+
if (e && e.pyodide_fatal_error) {
|
|
197
225
|
throw e;
|
|
198
226
|
}
|
|
199
227
|
if (e instanceof Module._PropagatePythonError) {
|
|
@@ -207,6 +235,16 @@ Module.handle_js_error = function (e: any) {
|
|
|
207
235
|
// Try to restore the original Python exception.
|
|
208
236
|
restored_error = Module._restore_sys_last_exception(e.__error_address);
|
|
209
237
|
}
|
|
238
|
+
let stack: any;
|
|
239
|
+
let weirdCatch;
|
|
240
|
+
try {
|
|
241
|
+
stack = ErrorStackParser.parse(e);
|
|
242
|
+
} catch (_) {
|
|
243
|
+
weirdCatch = true;
|
|
244
|
+
}
|
|
245
|
+
if (weirdCatch) {
|
|
246
|
+
e = ensureCaughtObjectIsError(e);
|
|
247
|
+
}
|
|
210
248
|
if (!restored_error) {
|
|
211
249
|
// Wrap the JavaScript error
|
|
212
250
|
let eidx = Hiwire.new_value(e);
|
|
@@ -215,7 +253,10 @@ Module.handle_js_error = function (e: any) {
|
|
|
215
253
|
Module._Py_DecRef(err);
|
|
216
254
|
Hiwire.decref(eidx);
|
|
217
255
|
}
|
|
218
|
-
|
|
256
|
+
if (weirdCatch) {
|
|
257
|
+
// In this case we have no stack frames so we can quit
|
|
258
|
+
return;
|
|
259
|
+
}
|
|
219
260
|
if (isErrorStart(stack[0])) {
|
|
220
261
|
while (isPyodideFrame(stack[0])) {
|
|
221
262
|
stack.shift();
|
package/index.test-d.ts
CHANGED
|
@@ -159,4 +159,9 @@ async function main() {
|
|
|
159
159
|
expectAssignable<{ done?: any; value: any }>(px.next());
|
|
160
160
|
expectAssignable<{ done?: any; value: any }>(px.next(22));
|
|
161
161
|
}
|
|
162
|
+
|
|
163
|
+
pyodide.unpackArchive(new Uint8Array(40), "tar");
|
|
164
|
+
pyodide.unpackArchive(new Uint8Array(40), "tar", {
|
|
165
|
+
extractDir: "/some/path",
|
|
166
|
+
});
|
|
162
167
|
}
|
package/load-package.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Module, API } from "./module.js";
|
|
1
|
+
import { Module, API, Tests } from "./module.js";
|
|
2
2
|
import { IN_NODE, nodeFsPromisesMod, _loadBinaryFile } from "./compat.js";
|
|
3
3
|
import { PyProxy, isPyProxy } from "./pyproxy.gen";
|
|
4
4
|
|
|
@@ -171,13 +171,14 @@ async function installPackage(name: string, buffer: Uint8Array) {
|
|
|
171
171
|
imports: [] as string[],
|
|
172
172
|
};
|
|
173
173
|
}
|
|
174
|
-
const
|
|
174
|
+
const filename = pkg.file_name;
|
|
175
175
|
// This Python helper function unpacks the buffer and lists out any so files therein.
|
|
176
|
-
const dynlibs = API.package_loader.unpack_buffer(
|
|
177
|
-
file_name,
|
|
176
|
+
const dynlibs = API.package_loader.unpack_buffer.callKwargs({
|
|
178
177
|
buffer,
|
|
179
|
-
|
|
180
|
-
|
|
178
|
+
filename,
|
|
179
|
+
target: pkg.install_dir,
|
|
180
|
+
calculate_dynlibs: true,
|
|
181
|
+
});
|
|
181
182
|
for (const dynlib of dynlibs) {
|
|
182
183
|
await loadDynlib(dynlib, pkg.shared_library);
|
|
183
184
|
}
|
|
@@ -248,10 +249,19 @@ async function loadDynlib(lib: string, shared: boolean) {
|
|
|
248
249
|
nodelete: true,
|
|
249
250
|
});
|
|
250
251
|
}
|
|
252
|
+
} catch (e) {
|
|
253
|
+
if (e.message.includes("need to see wasm magic number")) {
|
|
254
|
+
console.warn(
|
|
255
|
+
`Failed to load dynlib ${lib}. We probably just tried to load a linux .so file or something.`
|
|
256
|
+
);
|
|
257
|
+
return;
|
|
258
|
+
}
|
|
259
|
+
throw e;
|
|
251
260
|
} finally {
|
|
252
261
|
releaseDynlibLock();
|
|
253
262
|
}
|
|
254
263
|
}
|
|
264
|
+
Tests.loadDynlib = loadDynlib;
|
|
255
265
|
|
|
256
266
|
const acquirePackageLock = createLock();
|
|
257
267
|
|
|
@@ -353,6 +363,7 @@ export async function loadPackage(
|
|
|
353
363
|
loadedPackages[name] = channel;
|
|
354
364
|
})
|
|
355
365
|
.catch((err) => {
|
|
366
|
+
console.warn(err);
|
|
356
367
|
failed[name] = err;
|
|
357
368
|
});
|
|
358
369
|
}
|
|
@@ -366,6 +377,7 @@ export async function loadPackage(
|
|
|
366
377
|
loadedPackages[name] = channel;
|
|
367
378
|
})
|
|
368
379
|
.catch((err) => {
|
|
380
|
+
console.warn(err);
|
|
369
381
|
failed[name] = err;
|
|
370
382
|
});
|
|
371
383
|
}
|
package/package.json
CHANGED
package/pyproxy.gen.ts
CHANGED
|
@@ -21,6 +21,90 @@
|
|
|
21
21
|
|
|
22
22
|
|
|
23
23
|
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
|
|
24
108
|
/**
|
|
25
109
|
* Every public Python entrypoint goes through this file! The main entrypoint is
|
|
26
110
|
* the callPyObject method, but of course one can also execute arbitrary code
|
|
@@ -289,7 +373,12 @@ Module.callPyObjectKwargs = function (ptrobj: number, ...jsargs: any) {
|
|
|
289
373
|
if (idresult === 0) {
|
|
290
374
|
Module._pythonexc2js();
|
|
291
375
|
}
|
|
292
|
-
|
|
376
|
+
let result = Hiwire.pop_value(idresult);
|
|
377
|
+
// Automatically schedule coroutines
|
|
378
|
+
if (result && result.type === "coroutine" && result._ensure_future) {
|
|
379
|
+
result._ensure_future();
|
|
380
|
+
}
|
|
381
|
+
return result;
|
|
293
382
|
};
|
|
294
383
|
Module.callPyObject = function (ptrobj: number, ...jsargs: any) {
|
|
295
384
|
return Module.callPyObjectKwargs(ptrobj, ...jsargs, {});
|