pyodide 0.18.2 → 0.19.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/README.md CHANGED
@@ -1,4 +1,4 @@
1
- # Pyodide Javascript package
1
+ # Pyodide JavaScript package
2
2
 
3
3
  <a href="https://www.npmjs.com/package/pyodide"><img src="https://img.shields.io/npm/v/pyodide" alt="npm"></a>
4
4
 
@@ -26,15 +26,15 @@ See the [documentation](https://pyodide.org/en/stable/) fore more details.
26
26
 
27
27
  ## Details
28
28
 
29
- The Javascript code in this package is responsible for the following tasks:
29
+ The JavaScript code in this package is responsible for the following tasks:
30
30
 
31
- 1. Defines the public [Javascript API](https://pyodide.org/en/stable/usage/api/js-api.html)
31
+ 1. Defines the public [JavaScript API](https://pyodide.org/en/stable/usage/api/js-api.html)
32
32
  - Package loading code to allow loading of other Python packages.
33
33
  - Can load
34
34
  [micropip](https://pyodide.org/en/stable/usage/api/micropip-api.html) to
35
35
  bootstrap loading of pure Python wheels
36
36
  2. Loads the CPython interpreter and the core/pyodide emscripten application
37
37
  which embeds the interpreter.
38
- 3. Injects the `js/pyodide` Javascript API into `sys.modules`. This is the
38
+ 3. Injects the `js/pyodide` JavaScript API into `sys.modules`. This is the
39
39
  final runtime dependency for `core/pyodide` & `py/pyodide`, so after this step
40
40
  the interpreter is fully up and running.
package/api.js CHANGED
@@ -8,17 +8,18 @@ export { loadPackage, loadedPackages, isPyProxy };
8
8
  * @typedef {import('./pyproxy.gen').PyProxy} PyProxy
9
9
  * @typedef {import('./pyproxy.gen').TypedArray} TypedArray
10
10
  * @typedef {import('emscripten')} Emscripten
11
+ * @typedef {import('emscripten').Module.FS} FS
11
12
  */
12
13
 
13
14
  /**
14
15
  * An alias to the Python :py:mod:`pyodide` package.
15
16
  *
16
17
  * You can use this to call functions defined in the Pyodide Python package
17
- * from Javascript.
18
+ * from JavaScript.
18
19
  *
19
20
  * @type {PyProxy}
20
21
  */
21
- let pyodide_py = {}; // actually defined in runPythonSimple in loadPyodide (see pyodide.js)
22
+ let pyodide_py = {}; // actually defined in loadPyodide (see pyodide.js)
22
23
 
23
24
  /**
24
25
  *
@@ -29,10 +30,10 @@ let pyodide_py = {}; // actually defined in runPythonSimple in loadPyodide (see
29
30
  *
30
31
  * @type {PyProxy}
31
32
  */
32
- let globals = {}; // actually defined in runPythonSimple in loadPyodide (see pyodide.js)
33
+ let globals = {}; // actually defined in loadPyodide (see pyodide.js)
33
34
 
34
35
  /**
35
- * A Javascript error caused by a Python exception.
36
+ * A JavaScript error caused by a Python exception.
36
37
  *
37
38
  * In order to reduce the risk of large memory leaks, the ``PythonError``
38
39
  * contains no reference to the Python exception that caused it. You can find
@@ -74,19 +75,19 @@ export class PythonError {
74
75
  *
75
76
  * @type {string}
76
77
  */
77
- export let version = ""; // actually defined in runPythonSimple in loadPyodide (see pyodide.js)
78
+ export let version = ""; // actually defined in loadPyodide (see pyodide.js)
78
79
 
79
80
  /**
80
- * Runs a string of Python code from Javascript.
81
+ * Runs a string of Python code from JavaScript.
81
82
  *
82
83
  * The last part of the string may be an expression, in which case, its value
83
84
  * is returned.
84
85
  *
85
86
  * @param {string} code Python code to evaluate
86
- * @param {PyProxy} globals An optional Python dictionary to use as the globals.
87
+ * @param {PyProxy=} globals An optional Python dictionary to use as the globals.
87
88
  * Defaults to :any:`pyodide.globals`. Uses the Python API
88
89
  * :any:`pyodide.eval_code` to evaluate the code.
89
- * @returns {Py2JsResult} The result of the Python code translated to Javascript. See the
90
+ * @returns {Py2JsResult} The result of the Python code translated to JavaScript. See the
90
91
  * documentation for :any:`pyodide.eval_code` for more info.
91
92
  */
92
93
  export function runPython(code, globals = Module.globals) {
@@ -150,22 +151,6 @@ export async function loadPackagesFromImports(
150
151
  }
151
152
  }
152
153
 
153
- /**
154
- * Access a Python object in the global namespace from Javascript.
155
- *
156
- * @deprecated This function will be removed in version 0.18.0. Use
157
- * :any:`pyodide.globals.get('key') <pyodide.globals>` instead.
158
- *
159
- * @param {string} name Python variable name
160
- * @returns {Py2JsResult} The Python object translated to Javascript.
161
- */
162
- export function pyimport(name) {
163
- console.warn(
164
- "Access to the Python global namespace via pyodide.pyimport is deprecated and " +
165
- "will be removed in version 0.18.0. Use pyodide.globals.get('key') instead."
166
- );
167
- return Module.globals.get(name);
168
- }
169
154
  /**
170
155
  * Runs Python code using `PyCF_ALLOW_TOP_LEVEL_AWAIT
171
156
  * <https://docs.python.org/3/library/ast.html?highlight=pycf_allow_top_level_await#ast.PyCF_ALLOW_TOP_LEVEL_AWAIT>`_.
@@ -185,36 +170,33 @@ export function pyimport(name) {
185
170
  * from js import fetch
186
171
  * response = await fetch("./packages.json")
187
172
  * packages = await response.json()
188
- * # If final statement is an expression, its value is returned to Javascript
173
+ * # If final statement is an expression, its value is returned to JavaScript
189
174
  * len(packages.packages.object_keys())
190
175
  * `);
191
176
  * console.log(result); // 79
192
177
  *
193
178
  * @param {string} code Python code to evaluate
194
- * @returns {Py2JsResult} The result of the Python code translated to Javascript.
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.
195
183
  * @async
196
184
  */
197
- export async function runPythonAsync(code) {
198
- let coroutine = Module.pyodide_py.eval_code_async(code, Module.globals);
199
- try {
200
- let result = await coroutine;
201
- return result;
202
- } finally {
203
- coroutine.destroy();
204
- }
185
+ export async function runPythonAsync(code, globals = Module.globals) {
186
+ return await Module.pyodide_py.eval_code_async(code, globals);
205
187
  }
206
188
  Module.runPythonAsync = runPythonAsync;
207
189
 
208
190
  /**
209
- * Registers the Javascript object ``module`` as a Javascript module named
191
+ * Registers the JavaScript object ``module`` as a JavaScript module named
210
192
  * ``name``. This module can then be imported from Python using the standard
211
193
  * Python import system. If another module by the same name has already been
212
194
  * imported, this won't have much effect unless you also delete the imported
213
195
  * module from ``sys.modules``. This calls the ``pyodide_py`` API
214
196
  * :func:`pyodide.register_js_module`.
215
197
  *
216
- * @param {string} name Name of the Javascript module to add
217
- * @param {object} module Javascript object backing the module
198
+ * @param {string} name Name of the JavaScript module to add
199
+ * @param {object} module JavaScript object backing the module
218
200
  */
219
201
  export function registerJsModule(name, module) {
220
202
  Module.pyodide_py.register_js_module(name, module);
@@ -229,24 +211,24 @@ export function registerComlink(Comlink) {
229
211
  }
230
212
 
231
213
  /**
232
- * Unregisters a Javascript module with given name that has been previously
214
+ * Unregisters a JavaScript module with given name that has been previously
233
215
  * registered with :js:func:`pyodide.registerJsModule` or
234
- * :func:`pyodide.register_js_module`. If a Javascript module with that name
216
+ * :func:`pyodide.register_js_module`. If a JavaScript module with that name
235
217
  * does not already exist, will throw an error. Note that if the module has
236
218
  * already been imported, this won't have much effect unless you also delete
237
219
  * the imported module from ``sys.modules``. This calls the ``pyodide_py`` API
238
220
  * :func:`pyodide.unregister_js_module`.
239
221
  *
240
- * @param {string} name Name of the Javascript module to remove
222
+ * @param {string} name Name of the JavaScript module to remove
241
223
  */
242
224
  export function unregisterJsModule(name) {
243
225
  Module.pyodide_py.unregister_js_module(name);
244
226
  }
245
227
 
246
228
  /**
247
- * Convert the Javascript object to a Python object as best as possible.
229
+ * Convert the JavaScript object to a Python object as best as possible.
248
230
  *
249
- * This is similar to :any:`JsProxy.to_py` but for use from Javascript. If the
231
+ * This is similar to :any:`JsProxy.to_py` but for use from JavaScript. If the
250
232
  * object is immutable or a :any:`PyProxy`, it will be returned unchanged. If
251
233
  * the object cannot be converted into Python, it will be returned unchanged.
252
234
  *
@@ -254,7 +236,7 @@ export function unregisterJsModule(name) {
254
236
  *
255
237
  * @param {*} obj
256
238
  * @param {object} options
257
- * @param {number} options.depth Optional argument to limit the depth of the
239
+ * @param {number=} options.depth Optional argument to limit the depth of the
258
240
  * conversion.
259
241
  * @returns {PyProxy} The object converted to Python.
260
242
  */
@@ -277,9 +259,13 @@ export function toPy(obj, { depth = -1 } = {}) {
277
259
  let result = 0;
278
260
  try {
279
261
  obj_id = Module.hiwire.new_value(obj);
280
- py_result = Module.__js2python_convert(obj_id, new Map(), depth);
281
- if (py_result === 0) {
282
- Module._pythonexc2js();
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;
283
269
  }
284
270
  if (Module._JsProxy_Check(py_result)) {
285
271
  // Oops, just created a JsProxy. Return the original object.
@@ -297,6 +283,56 @@ export function toPy(obj, { depth = -1 } = {}) {
297
283
  return Module.hiwire.pop_value(result);
298
284
  }
299
285
 
286
+ /**
287
+ * Imports a module and returns it.
288
+ * Warning: this function has a completely different behavior than the old removed pyimport function!
289
+ * ``pyimport`` is roughly equivalent to:
290
+ *
291
+ * .. code-block:: pyodide
292
+ *
293
+ * pyodide.runPython(`import ${pkgname}; ${pkgname}`);
294
+ *
295
+ * except that the global namespace will not change.
296
+ * Example:
297
+ *
298
+ * .. code-block:: pyodide
299
+ *
300
+ * let sysmodule = pyodide.pyimport("sys");
301
+ * let recursionLimit = sys.getrecursionlimit();
302
+ *
303
+ * The best way to run Python code with Pyodide is
304
+ * 1. write a Python package,
305
+ * 2. load your Python package into the Pyodide (Emscripten) file system,
306
+ * 3. import the package with `let mypkg = pyodide.pyimport("mypkgname")`,
307
+ * 4. call into your package with `mypkg.some_api(some_args)`.
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
+
300
336
  /**
301
337
  * @private
302
338
  */
@@ -308,11 +344,34 @@ Module.saveState = () => Module.pyodide_py._state.save_state();
308
344
  Module.restoreState = (state) => Module.pyodide_py._state.restore_state(state);
309
345
 
310
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
+ *
311
353
  * @param {TypedArray} interrupt_buffer
312
354
  */
313
- function setInterruptBuffer(interrupt_buffer) {}
314
- setInterruptBuffer = Module.setInterruptBuffer;
315
- export { setInterruptBuffer };
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
+ }
316
375
 
317
376
  export function makePublicAPI() {
318
377
  /**
@@ -325,15 +384,14 @@ export function makePublicAPI() {
325
384
  * which can be used to extend the in-memory filesystem with features like `persistence
326
385
  * <https://emscripten.org/docs/api_reference/Filesystem-API.html#persistent-data>`_.
327
386
  *
328
- * While all of the file systems implementations are enabled, only the default
387
+ * While all the file systems implementations are enabled, only the default
329
388
  * ``MEMFS`` is guaranteed to work in all runtime settings. The implementations
330
389
  * are available as members of ``FS.filesystems``:
331
390
  * ``IDBFS``, ``NODEFS``, ``PROXYFS``, ``WORKERFS``.
332
391
  *
333
- * @type {FS} The Emscripten File System API.
392
+ * @type {FS}
334
393
  */
335
394
  const FS = Module.FS;
336
-
337
395
  let namespace = {
338
396
  globals,
339
397
  FS,
@@ -343,13 +401,15 @@ export function makePublicAPI() {
343
401
  loadPackagesFromImports,
344
402
  loadedPackages,
345
403
  isPyProxy,
346
- pyimport,
347
404
  runPython,
348
405
  runPythonAsync,
349
406
  registerJsModule,
350
407
  unregisterJsModule,
351
408
  setInterruptBuffer,
409
+ checkInterrupt,
352
410
  toPy,
411
+ pyimport,
412
+ unpackArchive,
353
413
  registerComlink,
354
414
  PythonError,
355
415
  PyBuffer,
package/index.test-d.ts CHANGED
@@ -27,8 +27,8 @@ async function main() {
27
27
  indexURL: "blah",
28
28
  fullStdLib: true,
29
29
  stdin: () => "a string",
30
- stdout: (x) => {},
31
- stderr: (err) => {},
30
+ stdout: (x: string) => {},
31
+ stderr: (err: string) => {},
32
32
  })
33
33
  );
34
34
 
@@ -49,29 +49,32 @@ async function main() {
49
49
 
50
50
  expectType<Promise<void>>(pyodide.loadPackagesFromImports("import some_pkg"));
51
51
  expectType<Promise<void>>(
52
- pyodide.loadPackagesFromImports("import some_pkg", (x) => console.log(x))
52
+ pyodide.loadPackagesFromImports("import some_pkg", (x: any) =>
53
+ console.log(x)
54
+ )
53
55
  );
54
56
  expectType<Promise<void>>(
55
57
  pyodide.loadPackagesFromImports(
56
58
  "import some_pkg",
57
- (x) => console.log(x),
58
- (x) => console.warn(x)
59
+ (x: any) => console.log(x),
60
+ (x: any) => console.warn(x)
59
61
  )
60
62
  );
61
63
 
62
64
  expectType<Promise<void>>(pyodide.loadPackage("blah"));
63
65
  expectType<Promise<void>>(pyodide.loadPackage(["blah", "blah2"]));
64
- expectType<Promise<void>>(pyodide.loadPackage("blah", (x) => console.log(x)));
66
+ expectType<Promise<void>>(
67
+ pyodide.loadPackage("blah", (x: any) => console.log(x))
68
+ );
65
69
  expectType<Promise<void>>(
66
70
  pyodide.loadPackage(
67
71
  ["blah", "blah2"],
68
- (x) => console.log(x),
69
- (x) => console.warn(x)
72
+ (x: any) => console.log(x),
73
+ (x: any) => console.warn(x)
70
74
  )
71
75
  );
72
76
  expectType<Promise<void>>(pyodide.loadPackage(px));
73
77
 
74
- expectType<Py2JsResult>(pyodide.pyimport("blah"));
75
78
  expectType<PyProxy>(pyodide.pyodide_py);
76
79
  expectType<void>(pyodide.registerJsModule("blah", { a: 7 }));
77
80
  expectType<void>(pyodide.unregisterJsModule("blah"));