pyodide 0.19.1 → 0.20.0-alpha.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/api.ts +459 -0
- package/compat.ts +146 -0
- package/index.test-d.ts +20 -20
- package/load-package.ts +402 -0
- package/{module.js → module.ts} +21 -13
- package/package.json +13 -6
- package/pyodide.ts +309 -0
- package/pyodide.umd.ts +3 -0
- package/rollup.config.js +16 -4
- package/test/conftest.js +1 -1
- package/tsconfig.json +8 -6
- package/api.js +0 -421
- package/load-pyodide.js +0 -426
- package/pyodide.js +0 -302
package/api.js
DELETED
|
@@ -1,421 +0,0 @@
|
|
|
1
|
-
import { Module } from "./module.js";
|
|
2
|
-
import { loadPackage, loadedPackages } from "./load-pyodide.js";
|
|
3
|
-
import { isPyProxy, PyBuffer } from "./pyproxy.gen.js";
|
|
4
|
-
export { loadPackage, loadedPackages, isPyProxy };
|
|
5
|
-
|
|
6
|
-
/**
|
|
7
|
-
* @typedef {import('./pyproxy.gen').Py2JsResult} Py2JsResult
|
|
8
|
-
* @typedef {import('./pyproxy.gen').PyProxy} PyProxy
|
|
9
|
-
* @typedef {import('./pyproxy.gen').TypedArray} TypedArray
|
|
10
|
-
* @typedef {import('emscripten')} Emscripten
|
|
11
|
-
* @typedef {import('emscripten').Module.FS} FS
|
|
12
|
-
*/
|
|
13
|
-
|
|
14
|
-
/**
|
|
15
|
-
* An alias to the Python :py:mod:`pyodide` package.
|
|
16
|
-
*
|
|
17
|
-
* You can use this to call functions defined in the Pyodide Python package
|
|
18
|
-
* from JavaScript.
|
|
19
|
-
*
|
|
20
|
-
* @type {PyProxy}
|
|
21
|
-
*/
|
|
22
|
-
let pyodide_py = {}; // actually defined in loadPyodide (see pyodide.js)
|
|
23
|
-
|
|
24
|
-
/**
|
|
25
|
-
*
|
|
26
|
-
* An alias to the global Python namespace.
|
|
27
|
-
*
|
|
28
|
-
* For example, to access a variable called ``foo`` in the Python global
|
|
29
|
-
* scope, use ``pyodide.globals.get("foo")``
|
|
30
|
-
*
|
|
31
|
-
* @type {PyProxy}
|
|
32
|
-
*/
|
|
33
|
-
let globals = {}; // actually defined in loadPyodide (see pyodide.js)
|
|
34
|
-
|
|
35
|
-
/**
|
|
36
|
-
* A JavaScript error caused by a Python exception.
|
|
37
|
-
*
|
|
38
|
-
* In order to reduce the risk of large memory leaks, the ``PythonError``
|
|
39
|
-
* contains no reference to the Python exception that caused it. You can find
|
|
40
|
-
* the actual Python exception that caused this error as `sys.last_value
|
|
41
|
-
* <https://docs.python.org/3/library/sys.html#sys.last_value>`_.
|
|
42
|
-
*
|
|
43
|
-
* See :ref:`type-translations-errors` for more information.
|
|
44
|
-
*
|
|
45
|
-
* .. admonition:: Avoid Stack Frames
|
|
46
|
-
* :class: warning
|
|
47
|
-
*
|
|
48
|
-
* If you make a :any:`PyProxy` of ``sys.last_value``, you should be
|
|
49
|
-
* especially careful to :any:`destroy() <PyProxy.destroy>` it when you are
|
|
50
|
-
* done. You may leak a large amount of memory including the local
|
|
51
|
-
* variables of all the stack frames in the traceback if you don't. The
|
|
52
|
-
* easiest way is to only handle the exception in Python.
|
|
53
|
-
*
|
|
54
|
-
* @class
|
|
55
|
-
*/
|
|
56
|
-
export class PythonError {
|
|
57
|
-
// actually defined in error_handling.c. TODO: would be good to move this
|
|
58
|
-
// documentation and the definition of PythonError to error_handling.js
|
|
59
|
-
constructor() {
|
|
60
|
-
/**
|
|
61
|
-
* The Python traceback.
|
|
62
|
-
* @type {string}
|
|
63
|
-
*/
|
|
64
|
-
this.message;
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
/**
|
|
69
|
-
*
|
|
70
|
-
* The Pyodide version.
|
|
71
|
-
*
|
|
72
|
-
* It can be either the exact release version (e.g. ``0.1.0``), or
|
|
73
|
-
* the latest release version followed by the number of commits since, and
|
|
74
|
-
* the git hash of the current commit (e.g. ``0.1.0-1-bd84646``).
|
|
75
|
-
*
|
|
76
|
-
* @type {string}
|
|
77
|
-
*/
|
|
78
|
-
export let version = ""; // actually defined in loadPyodide (see pyodide.js)
|
|
79
|
-
|
|
80
|
-
/**
|
|
81
|
-
* Runs a string of Python code from JavaScript.
|
|
82
|
-
*
|
|
83
|
-
* The last part of the string may be an expression, in which case, its value
|
|
84
|
-
* is returned.
|
|
85
|
-
*
|
|
86
|
-
* @param {string} code Python code to evaluate
|
|
87
|
-
* @param {PyProxy=} globals An optional Python dictionary to use as the globals.
|
|
88
|
-
* Defaults to :any:`pyodide.globals`. Uses the Python API
|
|
89
|
-
* :any:`pyodide.eval_code` to evaluate the code.
|
|
90
|
-
* @returns {Py2JsResult} The result of the Python code translated to JavaScript. See the
|
|
91
|
-
* documentation for :any:`pyodide.eval_code` for more info.
|
|
92
|
-
*/
|
|
93
|
-
export function runPython(code, globals = Module.globals) {
|
|
94
|
-
return Module.pyodide_py.eval_code(code, globals);
|
|
95
|
-
}
|
|
96
|
-
Module.runPython = runPython;
|
|
97
|
-
|
|
98
|
-
/**
|
|
99
|
-
* @callback LogFn
|
|
100
|
-
* @param {string} msg
|
|
101
|
-
* @returns {void}
|
|
102
|
-
* @private
|
|
103
|
-
*/
|
|
104
|
-
|
|
105
|
-
/**
|
|
106
|
-
* Inspect a Python code chunk and use :js:func:`pyodide.loadPackage` to install
|
|
107
|
-
* any known packages that the code chunk imports. Uses the Python API
|
|
108
|
-
* :func:`pyodide.find\_imports` to inspect the code.
|
|
109
|
-
*
|
|
110
|
-
* For example, given the following code as input
|
|
111
|
-
*
|
|
112
|
-
* .. code-block:: python
|
|
113
|
-
*
|
|
114
|
-
* import numpy as np x = np.array([1, 2, 3])
|
|
115
|
-
*
|
|
116
|
-
* :js:func:`loadPackagesFromImports` will call
|
|
117
|
-
* ``pyodide.loadPackage(['numpy'])``.
|
|
118
|
-
*
|
|
119
|
-
* @param {string} code The code to inspect.
|
|
120
|
-
* @param {LogFn=} messageCallback The ``messageCallback`` argument of
|
|
121
|
-
* :any:`pyodide.loadPackage` (optional).
|
|
122
|
-
* @param {LogFn=} errorCallback The ``errorCallback`` argument of
|
|
123
|
-
* :any:`pyodide.loadPackage` (optional).
|
|
124
|
-
* @async
|
|
125
|
-
*/
|
|
126
|
-
export async function loadPackagesFromImports(
|
|
127
|
-
code,
|
|
128
|
-
messageCallback,
|
|
129
|
-
errorCallback
|
|
130
|
-
) {
|
|
131
|
-
let pyimports = Module.pyodide_py.find_imports(code);
|
|
132
|
-
let imports;
|
|
133
|
-
try {
|
|
134
|
-
imports = pyimports.toJs();
|
|
135
|
-
} finally {
|
|
136
|
-
pyimports.destroy();
|
|
137
|
-
}
|
|
138
|
-
if (imports.length === 0) {
|
|
139
|
-
return;
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
let packageNames = Module._import_name_to_package_name;
|
|
143
|
-
let packages = new Set();
|
|
144
|
-
for (let name of imports) {
|
|
145
|
-
if (packageNames.has(name)) {
|
|
146
|
-
packages.add(packageNames.get(name));
|
|
147
|
-
}
|
|
148
|
-
}
|
|
149
|
-
if (packages.size) {
|
|
150
|
-
await loadPackage(Array.from(packages), messageCallback, errorCallback);
|
|
151
|
-
}
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
/**
|
|
155
|
-
* Runs Python code using `PyCF_ALLOW_TOP_LEVEL_AWAIT
|
|
156
|
-
* <https://docs.python.org/3/library/ast.html?highlight=pycf_allow_top_level_await#ast.PyCF_ALLOW_TOP_LEVEL_AWAIT>`_.
|
|
157
|
-
*
|
|
158
|
-
* .. admonition:: Python imports
|
|
159
|
-
* :class: warning
|
|
160
|
-
*
|
|
161
|
-
* Since pyodide 0.18.0, you must call :js:func:`loadPackagesFromImports` to
|
|
162
|
-
* import any python packages referenced via `import` statements in your code.
|
|
163
|
-
* This function will no longer do it for you.
|
|
164
|
-
*
|
|
165
|
-
* For example:
|
|
166
|
-
*
|
|
167
|
-
* .. code-block:: pyodide
|
|
168
|
-
*
|
|
169
|
-
* let result = await pyodide.runPythonAsync(`
|
|
170
|
-
* from js import fetch
|
|
171
|
-
* response = await fetch("./packages.json")
|
|
172
|
-
* packages = await response.json()
|
|
173
|
-
* # If final statement is an expression, its value is returned to JavaScript
|
|
174
|
-
* len(packages.packages.object_keys())
|
|
175
|
-
* `);
|
|
176
|
-
* console.log(result); // 79
|
|
177
|
-
*
|
|
178
|
-
* @param {string} code Python code to evaluate
|
|
179
|
-
* @param {PyProxy=} globals An optional Python dictionary to use as the globals.
|
|
180
|
-
* Defaults to :any:`pyodide.globals`. Uses the Python API
|
|
181
|
-
* :any:`pyodide.eval_code_async` to evaluate the code.
|
|
182
|
-
* @returns {Py2JsResult} The result of the Python code translated to JavaScript.
|
|
183
|
-
* @async
|
|
184
|
-
*/
|
|
185
|
-
export async function runPythonAsync(code, globals = Module.globals) {
|
|
186
|
-
return await Module.pyodide_py.eval_code_async(code, globals);
|
|
187
|
-
}
|
|
188
|
-
Module.runPythonAsync = runPythonAsync;
|
|
189
|
-
|
|
190
|
-
/**
|
|
191
|
-
* Registers the JavaScript object ``module`` as a JavaScript module named
|
|
192
|
-
* ``name``. This module can then be imported from Python using the standard
|
|
193
|
-
* Python import system. If another module by the same name has already been
|
|
194
|
-
* imported, this won't have much effect unless you also delete the imported
|
|
195
|
-
* module from ``sys.modules``. This calls the ``pyodide_py`` API
|
|
196
|
-
* :func:`pyodide.register_js_module`.
|
|
197
|
-
*
|
|
198
|
-
* @param {string} name Name of the JavaScript module to add
|
|
199
|
-
* @param {object} module JavaScript object backing the module
|
|
200
|
-
*/
|
|
201
|
-
export function registerJsModule(name, module) {
|
|
202
|
-
Module.pyodide_py.register_js_module(name, module);
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
/**
|
|
206
|
-
* Tell Pyodide about Comlink.
|
|
207
|
-
* Necessary to enable importing Comlink proxies into Python.
|
|
208
|
-
*/
|
|
209
|
-
export function registerComlink(Comlink) {
|
|
210
|
-
Module._Comlink = Comlink;
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
/**
|
|
214
|
-
* Unregisters a JavaScript module with given name that has been previously
|
|
215
|
-
* registered with :js:func:`pyodide.registerJsModule` or
|
|
216
|
-
* :func:`pyodide.register_js_module`. If a JavaScript module with that name
|
|
217
|
-
* does not already exist, will throw an error. Note that if the module has
|
|
218
|
-
* already been imported, this won't have much effect unless you also delete
|
|
219
|
-
* the imported module from ``sys.modules``. This calls the ``pyodide_py`` API
|
|
220
|
-
* :func:`pyodide.unregister_js_module`.
|
|
221
|
-
*
|
|
222
|
-
* @param {string} name Name of the JavaScript module to remove
|
|
223
|
-
*/
|
|
224
|
-
export function unregisterJsModule(name) {
|
|
225
|
-
Module.pyodide_py.unregister_js_module(name);
|
|
226
|
-
}
|
|
227
|
-
|
|
228
|
-
/**
|
|
229
|
-
* Convert the JavaScript object to a Python object as best as possible.
|
|
230
|
-
*
|
|
231
|
-
* This is similar to :any:`JsProxy.to_py` but for use from JavaScript. If the
|
|
232
|
-
* object is immutable or a :any:`PyProxy`, it will be returned unchanged. If
|
|
233
|
-
* the object cannot be converted into Python, it will be returned unchanged.
|
|
234
|
-
*
|
|
235
|
-
* See :ref:`type-translations-jsproxy-to-py` for more information.
|
|
236
|
-
*
|
|
237
|
-
* @param {*} obj
|
|
238
|
-
* @param {object} options
|
|
239
|
-
* @param {number=} options.depth Optional argument to limit the depth of the
|
|
240
|
-
* conversion.
|
|
241
|
-
* @returns {PyProxy} The object converted to Python.
|
|
242
|
-
*/
|
|
243
|
-
export function toPy(obj, { depth = -1 } = {}) {
|
|
244
|
-
// No point in converting these, it'd be dumb to proxy them so they'd just
|
|
245
|
-
// get converted back by `js2python` at the end
|
|
246
|
-
switch (typeof obj) {
|
|
247
|
-
case "string":
|
|
248
|
-
case "number":
|
|
249
|
-
case "boolean":
|
|
250
|
-
case "bigint":
|
|
251
|
-
case "undefined":
|
|
252
|
-
return obj;
|
|
253
|
-
}
|
|
254
|
-
if (!obj || Module.isPyProxy(obj)) {
|
|
255
|
-
return obj;
|
|
256
|
-
}
|
|
257
|
-
let obj_id = 0;
|
|
258
|
-
let py_result = 0;
|
|
259
|
-
let result = 0;
|
|
260
|
-
try {
|
|
261
|
-
obj_id = Module.hiwire.new_value(obj);
|
|
262
|
-
try {
|
|
263
|
-
py_result = Module.js2python_convert(obj_id, new Map(), depth);
|
|
264
|
-
} catch (e) {
|
|
265
|
-
if (e instanceof Module._PropagatePythonError) {
|
|
266
|
-
Module._pythonexc2js();
|
|
267
|
-
}
|
|
268
|
-
throw e;
|
|
269
|
-
}
|
|
270
|
-
if (Module._JsProxy_Check(py_result)) {
|
|
271
|
-
// Oops, just created a JsProxy. Return the original object.
|
|
272
|
-
return obj;
|
|
273
|
-
// return Module.pyproxy_new(py_result);
|
|
274
|
-
}
|
|
275
|
-
result = Module._python2js(py_result);
|
|
276
|
-
if (result === 0) {
|
|
277
|
-
Module._pythonexc2js();
|
|
278
|
-
}
|
|
279
|
-
} finally {
|
|
280
|
-
Module.hiwire.decref(obj_id);
|
|
281
|
-
Module._Py_DecRef(py_result);
|
|
282
|
-
}
|
|
283
|
-
return Module.hiwire.pop_value(result);
|
|
284
|
-
}
|
|
285
|
-
|
|
286
|
-
/**
|
|
287
|
-
* Imports a module and returns it.
|
|
288
|
-
*
|
|
289
|
-
* .. admonition:: Warning
|
|
290
|
-
* :class: warning
|
|
291
|
-
*
|
|
292
|
-
* This function has a completely different behavior than the old removed pyimport function!
|
|
293
|
-
*
|
|
294
|
-
* ``pyimport`` is roughly equivalent to:
|
|
295
|
-
*
|
|
296
|
-
* .. code-block:: js
|
|
297
|
-
*
|
|
298
|
-
* pyodide.runPython(`import ${pkgname}; ${pkgname}`);
|
|
299
|
-
*
|
|
300
|
-
* except that the global namespace will not change.
|
|
301
|
-
*
|
|
302
|
-
* Example:
|
|
303
|
-
*
|
|
304
|
-
* .. code-block:: js
|
|
305
|
-
*
|
|
306
|
-
* let sysmodule = pyodide.pyimport("sys");
|
|
307
|
-
* let recursionLimit = sys.getrecursionlimit();
|
|
308
|
-
*
|
|
309
|
-
* @param {string} mod_name The name of the module to import
|
|
310
|
-
* @returns A PyProxy for the imported module
|
|
311
|
-
*/
|
|
312
|
-
export function pyimport(mod_name) {
|
|
313
|
-
return Module.importlib.import_module(mod_name);
|
|
314
|
-
}
|
|
315
|
-
|
|
316
|
-
/**
|
|
317
|
-
* Unpack an archive into a target directory.
|
|
318
|
-
*
|
|
319
|
-
* @param {ArrayBuffer} buffer The archive as an ArrayBuffer (it's also fine to pass a TypedArray).
|
|
320
|
-
* @param {string} format The format of the archive. Should be one of the formats recognized by `shutil.unpack_archive`.
|
|
321
|
-
* By default the options are 'bztar', 'gztar', 'tar', 'zip', and 'wheel'. Several synonyms are accepted for each format, e.g.,
|
|
322
|
-
* for 'gztar' any of '.gztar', '.tar.gz', '.tgz', 'tar.gz' or 'tgz' are considered to be synonyms.
|
|
323
|
-
*
|
|
324
|
-
* @param {string=} extract_dir The directory to unpack the archive into. Defaults to the working directory.
|
|
325
|
-
*/
|
|
326
|
-
export function unpackArchive(buffer, format, extract_dir) {
|
|
327
|
-
if (!Module._util_module) {
|
|
328
|
-
Module._util_module = pyimport("pyodide._util");
|
|
329
|
-
}
|
|
330
|
-
Module._util_module.unpack_buffer_archive.callKwargs(buffer, {
|
|
331
|
-
format,
|
|
332
|
-
extract_dir,
|
|
333
|
-
});
|
|
334
|
-
}
|
|
335
|
-
|
|
336
|
-
/**
|
|
337
|
-
* @private
|
|
338
|
-
*/
|
|
339
|
-
Module.saveState = () => Module.pyodide_py._state.save_state();
|
|
340
|
-
|
|
341
|
-
/**
|
|
342
|
-
* @private
|
|
343
|
-
*/
|
|
344
|
-
Module.restoreState = (state) => Module.pyodide_py._state.restore_state(state);
|
|
345
|
-
|
|
346
|
-
/**
|
|
347
|
-
* Sets the interrupt buffer to be `interrupt_buffer`. This is only useful when
|
|
348
|
-
* Pyodide is used in a webworker. The buffer should be a `SharedArrayBuffer`
|
|
349
|
-
* shared with the main browser thread (or another worker). To request an
|
|
350
|
-
* interrupt, a `2` should be written into `interrupt_buffer` (2 is the posix
|
|
351
|
-
* constant for SIGINT).
|
|
352
|
-
*
|
|
353
|
-
* @param {TypedArray} interrupt_buffer
|
|
354
|
-
*/
|
|
355
|
-
export function setInterruptBuffer(interrupt_buffer) {
|
|
356
|
-
Module.interrupt_buffer = interrupt_buffer;
|
|
357
|
-
Module._set_pyodide_callback(!!interrupt_buffer);
|
|
358
|
-
}
|
|
359
|
-
|
|
360
|
-
/**
|
|
361
|
-
* Throws a KeyboardInterrupt error if a KeyboardInterrupt has been requested
|
|
362
|
-
* via the interrupt buffer.
|
|
363
|
-
*
|
|
364
|
-
* This can be used to enable keyboard interrupts during execution of JavaScript
|
|
365
|
-
* code, just as `PyErr_CheckSignals` is used to enable keyboard interrupts
|
|
366
|
-
* during execution of C code.
|
|
367
|
-
*/
|
|
368
|
-
export function checkInterrupt() {
|
|
369
|
-
if (Module.interrupt_buffer[0] === 2) {
|
|
370
|
-
Module.interrupt_buffer[0] = 0;
|
|
371
|
-
Module._PyErr_SetInterrupt();
|
|
372
|
-
Module.runPython("");
|
|
373
|
-
}
|
|
374
|
-
}
|
|
375
|
-
|
|
376
|
-
export function makePublicAPI() {
|
|
377
|
-
/**
|
|
378
|
-
* An alias to the `Emscripten File System API
|
|
379
|
-
* <https://emscripten.org/docs/api_reference/Filesystem-API.html>`_.
|
|
380
|
-
*
|
|
381
|
-
* This provides a wide range of POSIX-`like` file/device operations, including
|
|
382
|
-
* `mount
|
|
383
|
-
* <https://emscripten.org/docs/api_reference/Filesystem-API.html#FS.mount>`_
|
|
384
|
-
* which can be used to extend the in-memory filesystem with features like `persistence
|
|
385
|
-
* <https://emscripten.org/docs/api_reference/Filesystem-API.html#persistent-data>`_.
|
|
386
|
-
*
|
|
387
|
-
* While all the file systems implementations are enabled, only the default
|
|
388
|
-
* ``MEMFS`` is guaranteed to work in all runtime settings. The implementations
|
|
389
|
-
* are available as members of ``FS.filesystems``:
|
|
390
|
-
* ``IDBFS``, ``NODEFS``, ``PROXYFS``, ``WORKERFS``.
|
|
391
|
-
*
|
|
392
|
-
* @type {FS}
|
|
393
|
-
*/
|
|
394
|
-
const FS = Module.FS;
|
|
395
|
-
let namespace = {
|
|
396
|
-
globals,
|
|
397
|
-
FS,
|
|
398
|
-
pyodide_py,
|
|
399
|
-
version,
|
|
400
|
-
loadPackage,
|
|
401
|
-
loadPackagesFromImports,
|
|
402
|
-
loadedPackages,
|
|
403
|
-
isPyProxy,
|
|
404
|
-
runPython,
|
|
405
|
-
runPythonAsync,
|
|
406
|
-
registerJsModule,
|
|
407
|
-
unregisterJsModule,
|
|
408
|
-
setInterruptBuffer,
|
|
409
|
-
checkInterrupt,
|
|
410
|
-
toPy,
|
|
411
|
-
pyimport,
|
|
412
|
-
unpackArchive,
|
|
413
|
-
registerComlink,
|
|
414
|
-
PythonError,
|
|
415
|
-
PyBuffer,
|
|
416
|
-
};
|
|
417
|
-
|
|
418
|
-
namespace._module = Module; // @private
|
|
419
|
-
Module.public_api = namespace;
|
|
420
|
-
return namespace;
|
|
421
|
-
}
|