pyodide 0.19.0 → 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 +495 -0
- package/compat.ts +146 -0
- package/error_handling.gen.ts +335 -0
- package/index.test-d.ts +25 -20
- package/load-package.ts +414 -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/{pyproxy.gen.js → pyproxy.gen.ts} +391 -443
- 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/pyodide.js
DELETED
|
@@ -1,302 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* The main bootstrap code for loading pyodide.
|
|
3
|
-
*/
|
|
4
|
-
import { Module, setStandardStreams, setHomeDirectory } from "./module.js";
|
|
5
|
-
import {
|
|
6
|
-
loadScript,
|
|
7
|
-
initializePackageIndex,
|
|
8
|
-
_fetchBinaryFile,
|
|
9
|
-
loadPackage,
|
|
10
|
-
} from "./load-pyodide.js";
|
|
11
|
-
import { makePublicAPI, registerJsModule } from "./api.js";
|
|
12
|
-
import "./pyproxy.gen.js";
|
|
13
|
-
|
|
14
|
-
/**
|
|
15
|
-
* @typedef {import('./pyproxy.gen').PyProxy} PyProxy
|
|
16
|
-
* @typedef {import('./pyproxy.gen').PyProxyWithLength} PyProxyWithLength
|
|
17
|
-
* @typedef {import('./pyproxy.gen').PyProxyWithGet} PyProxyWithGet
|
|
18
|
-
* @typedef {import('./pyproxy.gen').PyProxyWithSet} PyProxyWithSet
|
|
19
|
-
* @typedef {import('./pyproxy.gen').PyProxyWithHas} PyProxyWithHas
|
|
20
|
-
* @typedef {import('./pyproxy.gen').PyProxyIterable} PyProxyIterable
|
|
21
|
-
* @typedef {import('./pyproxy.gen').PyProxyIterator} PyProxyIterator
|
|
22
|
-
* @typedef {import('./pyproxy.gen').PyProxyAwaitable} PyProxyAwaitable
|
|
23
|
-
* @typedef {import('./pyproxy.gen').PyProxyBuffer} PyProxyBuffer
|
|
24
|
-
* @typedef {import('./pyproxy.gen').PyProxyCallable} PyProxyCallable
|
|
25
|
-
*
|
|
26
|
-
* @typedef {import('./pyproxy.gen').Py2JsResult} Py2JsResult
|
|
27
|
-
*
|
|
28
|
-
* @typedef {import('./pyproxy.gen').TypedArray} TypedArray
|
|
29
|
-
* @typedef {import('./pyproxy.gen').PyBuffer} PyBuffer
|
|
30
|
-
*/
|
|
31
|
-
|
|
32
|
-
/**
|
|
33
|
-
* Dump the Python traceback to the browser console.
|
|
34
|
-
*
|
|
35
|
-
* @private
|
|
36
|
-
*/
|
|
37
|
-
Module.dump_traceback = function () {
|
|
38
|
-
const fd_stdout = 1;
|
|
39
|
-
Module.__Py_DumpTraceback(fd_stdout, Module._PyGILState_GetThisThreadState());
|
|
40
|
-
};
|
|
41
|
-
|
|
42
|
-
let fatal_error_occurred = false;
|
|
43
|
-
/**
|
|
44
|
-
* Signal a fatal error.
|
|
45
|
-
*
|
|
46
|
-
* Dumps the Python traceback, shows a JavaScript traceback, and prints a clear
|
|
47
|
-
* message indicating a fatal error. It then dummies out the public API so that
|
|
48
|
-
* further attempts to use Pyodide will clearly indicate that Pyodide has failed
|
|
49
|
-
* and can no longer be used. pyodide._module is left accessible, and it is
|
|
50
|
-
* possible to continue using Pyodide for debugging purposes if desired.
|
|
51
|
-
*
|
|
52
|
-
* @argument e {Error} The cause of the fatal error.
|
|
53
|
-
* @private
|
|
54
|
-
*/
|
|
55
|
-
Module.fatal_error = function (e) {
|
|
56
|
-
if (e.pyodide_fatal_error) {
|
|
57
|
-
return;
|
|
58
|
-
}
|
|
59
|
-
if (fatal_error_occurred) {
|
|
60
|
-
console.error("Recursive call to fatal_error. Inner error was:");
|
|
61
|
-
console.error(e);
|
|
62
|
-
return;
|
|
63
|
-
}
|
|
64
|
-
// Mark e so we know not to handle it later in EM_JS wrappers
|
|
65
|
-
e.pyodide_fatal_error = true;
|
|
66
|
-
fatal_error_occurred = true;
|
|
67
|
-
console.error(
|
|
68
|
-
"Pyodide has suffered a fatal error. Please report this to the Pyodide maintainers."
|
|
69
|
-
);
|
|
70
|
-
console.error("The cause of the fatal error was:");
|
|
71
|
-
if (Module.inTestHoist) {
|
|
72
|
-
// Test hoist won't print the error object in a useful way so convert it to
|
|
73
|
-
// string.
|
|
74
|
-
console.error(e.toString());
|
|
75
|
-
console.error(e.stack);
|
|
76
|
-
} else {
|
|
77
|
-
console.error(e);
|
|
78
|
-
}
|
|
79
|
-
try {
|
|
80
|
-
Module.dump_traceback();
|
|
81
|
-
for (let key of Object.keys(Module.public_api)) {
|
|
82
|
-
if (key.startsWith("_") || key === "version") {
|
|
83
|
-
continue;
|
|
84
|
-
}
|
|
85
|
-
Object.defineProperty(Module.public_api, key, {
|
|
86
|
-
enumerable: true,
|
|
87
|
-
configurable: true,
|
|
88
|
-
get: () => {
|
|
89
|
-
throw new Error(
|
|
90
|
-
"Pyodide already fatally failed and can no longer be used."
|
|
91
|
-
);
|
|
92
|
-
},
|
|
93
|
-
});
|
|
94
|
-
}
|
|
95
|
-
if (Module.on_fatal) {
|
|
96
|
-
Module.on_fatal(e);
|
|
97
|
-
}
|
|
98
|
-
} catch (err2) {
|
|
99
|
-
console.error("Another error occurred while handling the fatal error:");
|
|
100
|
-
console.error(err2);
|
|
101
|
-
}
|
|
102
|
-
throw e;
|
|
103
|
-
};
|
|
104
|
-
|
|
105
|
-
let runPythonInternal_dict; // Initialized in finalizeBootstrap
|
|
106
|
-
/**
|
|
107
|
-
* Just like `runPython` except uses a different globals dict and gets
|
|
108
|
-
* `eval_code` from `_pyodide` so that it can work before `pyodide` is imported.
|
|
109
|
-
* @private
|
|
110
|
-
*/
|
|
111
|
-
Module.runPythonInternal = function (code) {
|
|
112
|
-
return Module._pyodide._base.eval_code(code, runPythonInternal_dict);
|
|
113
|
-
};
|
|
114
|
-
|
|
115
|
-
/**
|
|
116
|
-
* A proxy around globals that falls back to checking for a builtin if has or
|
|
117
|
-
* get fails to find a global with the given key. Note that this proxy is
|
|
118
|
-
* transparent to js2python: it won't notice that this wrapper exists at all and
|
|
119
|
-
* will translate this proxy to the globals dictionary.
|
|
120
|
-
* @private
|
|
121
|
-
*/
|
|
122
|
-
function wrapPythonGlobals(globals_dict, builtins_dict) {
|
|
123
|
-
return new Proxy(globals_dict, {
|
|
124
|
-
get(target, symbol) {
|
|
125
|
-
if (symbol === "get") {
|
|
126
|
-
return (key) => {
|
|
127
|
-
let result = target.get(key);
|
|
128
|
-
if (result === undefined) {
|
|
129
|
-
result = builtins_dict.get(key);
|
|
130
|
-
}
|
|
131
|
-
return result;
|
|
132
|
-
};
|
|
133
|
-
}
|
|
134
|
-
if (symbol === "has") {
|
|
135
|
-
return (key) => target.has(key) || builtins_dict.has(key);
|
|
136
|
-
}
|
|
137
|
-
return Reflect.get(target, symbol);
|
|
138
|
-
},
|
|
139
|
-
});
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
function unpackPyodidePy(pyodide_py_tar) {
|
|
143
|
-
const fileName = "/pyodide_py.tar";
|
|
144
|
-
let stream = Module.FS.open(fileName, "w");
|
|
145
|
-
Module.FS.write(
|
|
146
|
-
stream,
|
|
147
|
-
new Uint8Array(pyodide_py_tar),
|
|
148
|
-
0,
|
|
149
|
-
pyodide_py_tar.byteLength,
|
|
150
|
-
undefined,
|
|
151
|
-
true
|
|
152
|
-
);
|
|
153
|
-
Module.FS.close(stream);
|
|
154
|
-
const code_ptr = Module.stringToNewUTF8(`
|
|
155
|
-
import shutil
|
|
156
|
-
shutil.unpack_archive("/pyodide_py.tar", "/lib/python3.9/site-packages/")
|
|
157
|
-
del shutil
|
|
158
|
-
import importlib
|
|
159
|
-
importlib.invalidate_caches()
|
|
160
|
-
del importlib
|
|
161
|
-
`);
|
|
162
|
-
let errcode = Module._PyRun_SimpleString(code_ptr);
|
|
163
|
-
if (errcode) {
|
|
164
|
-
throw new Error("OOPS!");
|
|
165
|
-
}
|
|
166
|
-
Module._free(code_ptr);
|
|
167
|
-
Module.FS.unlink(fileName);
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
/**
|
|
171
|
-
* This function is called after the emscripten module is finished initializing,
|
|
172
|
-
* so eval_code is newly available.
|
|
173
|
-
* It finishes the bootstrap so that once it is complete, it is possible to use
|
|
174
|
-
* the core `pyodide` apis. (But package loading is not ready quite yet.)
|
|
175
|
-
* @private
|
|
176
|
-
*/
|
|
177
|
-
function finalizeBootstrap(config) {
|
|
178
|
-
// First make internal dict so that we can use runPythonInternal.
|
|
179
|
-
// runPythonInternal uses a separate namespace, so we don't pollute the main
|
|
180
|
-
// environment with variables from our setup.
|
|
181
|
-
runPythonInternal_dict = Module._pyodide._base.eval_code("{}");
|
|
182
|
-
Module.importlib = Module.runPythonInternal("import importlib; importlib");
|
|
183
|
-
let import_module = Module.importlib.import_module;
|
|
184
|
-
|
|
185
|
-
Module.sys = import_module("sys");
|
|
186
|
-
Module.sys.path.insert(0, config.homedir);
|
|
187
|
-
|
|
188
|
-
// Set up globals
|
|
189
|
-
let globals = Module.runPythonInternal("import __main__; __main__.__dict__");
|
|
190
|
-
let builtins = Module.runPythonInternal("import builtins; builtins.__dict__");
|
|
191
|
-
Module.globals = wrapPythonGlobals(globals, builtins);
|
|
192
|
-
|
|
193
|
-
// Set up key Javascript modules.
|
|
194
|
-
let importhook = Module._pyodide._importhook;
|
|
195
|
-
importhook.register_js_finder();
|
|
196
|
-
importhook.register_js_module("js", config.jsglobals);
|
|
197
|
-
|
|
198
|
-
let pyodide = makePublicAPI();
|
|
199
|
-
importhook.register_js_module("pyodide_js", pyodide);
|
|
200
|
-
|
|
201
|
-
// import pyodide_py. We want to ensure that as much stuff as possible is
|
|
202
|
-
// already set up before importing pyodide_py to simplify development of
|
|
203
|
-
// pyodide_py code (Otherwise it's very hard to keep track of which things
|
|
204
|
-
// aren't set up yet.)
|
|
205
|
-
Module.pyodide_py = import_module("pyodide");
|
|
206
|
-
Module.version = Module.pyodide_py.__version__;
|
|
207
|
-
|
|
208
|
-
// copy some last constants onto public API.
|
|
209
|
-
pyodide.pyodide_py = Module.pyodide_py;
|
|
210
|
-
pyodide.version = Module.version;
|
|
211
|
-
pyodide.globals = Module.globals;
|
|
212
|
-
return pyodide;
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
/**
|
|
216
|
-
* Load the main Pyodide wasm module and initialize it.
|
|
217
|
-
*
|
|
218
|
-
* Only one copy of Pyodide can be loaded in a given JavaScript global scope
|
|
219
|
-
* because Pyodide uses global variables to load packages. If an attempt is made
|
|
220
|
-
* to load a second copy of Pyodide, :any:`loadPyodide` will throw an error.
|
|
221
|
-
* (This can be fixed once `Firefox adopts support for ES6 modules in webworkers
|
|
222
|
-
* <https://bugzilla.mozilla.org/show_bug.cgi?id=1247687>`_.)
|
|
223
|
-
*
|
|
224
|
-
* @param {string} config.indexURL - The URL from which Pyodide will load
|
|
225
|
-
* packages
|
|
226
|
-
* @param {string} config.homedir - The home directory which Pyodide will use inside virtual file system
|
|
227
|
-
* Default: /home/pyodide
|
|
228
|
-
* @param {boolean} config.fullStdLib - Load the full Python standard library.
|
|
229
|
-
* Setting this to false excludes following modules: distutils.
|
|
230
|
-
* Default: true
|
|
231
|
-
* @param {undefined | function(): string} config.stdin - Override the standard input callback. Should ask the user for one line of input.
|
|
232
|
-
* Default: undefined
|
|
233
|
-
* @param {undefined | function(string)} config.stdout - Override the standard output callback.
|
|
234
|
-
* Default: undefined
|
|
235
|
-
* @param {undefined | function(string)} config.stderr - Override the standard error output callback.
|
|
236
|
-
* Default: undefined
|
|
237
|
-
* @returns The :ref:`js-api-pyodide` module.
|
|
238
|
-
* @memberof globalThis
|
|
239
|
-
* @async
|
|
240
|
-
*/
|
|
241
|
-
export async function loadPyodide(config) {
|
|
242
|
-
if (globalThis.__pyodide_module) {
|
|
243
|
-
throw new Error("Pyodide is already loading.");
|
|
244
|
-
}
|
|
245
|
-
if (!config.indexURL) {
|
|
246
|
-
throw new Error("Please provide indexURL parameter to loadPyodide");
|
|
247
|
-
}
|
|
248
|
-
|
|
249
|
-
loadPyodide.inProgress = true;
|
|
250
|
-
// A global "mount point" for the package loaders to talk to pyodide
|
|
251
|
-
// See "--export-name=__pyodide_module" in buildpkg.py
|
|
252
|
-
globalThis.__pyodide_module = Module;
|
|
253
|
-
|
|
254
|
-
const default_config = {
|
|
255
|
-
fullStdLib: true,
|
|
256
|
-
jsglobals: globalThis,
|
|
257
|
-
stdin: globalThis.prompt ? globalThis.prompt : undefined,
|
|
258
|
-
homedir: "/home/pyodide",
|
|
259
|
-
};
|
|
260
|
-
config = Object.assign(default_config, config);
|
|
261
|
-
|
|
262
|
-
if (!config.indexURL.endsWith("/")) {
|
|
263
|
-
config.indexURL += "/";
|
|
264
|
-
}
|
|
265
|
-
Module.indexURL = config.indexURL;
|
|
266
|
-
let packageIndexReady = initializePackageIndex(config.indexURL);
|
|
267
|
-
let pyodide_py_tar_promise = _fetchBinaryFile(
|
|
268
|
-
config.indexURL,
|
|
269
|
-
"pyodide_py.tar"
|
|
270
|
-
);
|
|
271
|
-
|
|
272
|
-
setStandardStreams(config.stdin, config.stdout, config.stderr);
|
|
273
|
-
setHomeDirectory(config.homedir);
|
|
274
|
-
|
|
275
|
-
let moduleLoaded = new Promise((r) => (Module.postRun = r));
|
|
276
|
-
|
|
277
|
-
const scriptSrc = `${config.indexURL}pyodide.asm.js`;
|
|
278
|
-
await loadScript(scriptSrc);
|
|
279
|
-
|
|
280
|
-
// _createPyodideModule is specified in the Makefile by the linker flag:
|
|
281
|
-
// `-s EXPORT_NAME="'_createPyodideModule'"`
|
|
282
|
-
await _createPyodideModule(Module);
|
|
283
|
-
|
|
284
|
-
// There is some work to be done between the module being "ready" and postRun
|
|
285
|
-
// being called.
|
|
286
|
-
await moduleLoaded;
|
|
287
|
-
|
|
288
|
-
const pyodide_py_tar = await pyodide_py_tar_promise;
|
|
289
|
-
unpackPyodidePy(pyodide_py_tar);
|
|
290
|
-
Module._pyodide_init();
|
|
291
|
-
|
|
292
|
-
let pyodide = finalizeBootstrap(config);
|
|
293
|
-
// Module.runPython works starting here.
|
|
294
|
-
|
|
295
|
-
await packageIndexReady;
|
|
296
|
-
if (config.fullStdLib) {
|
|
297
|
-
await loadPackage(["distutils"]);
|
|
298
|
-
}
|
|
299
|
-
pyodide.runPython("print('Python initialization complete')");
|
|
300
|
-
return pyodide;
|
|
301
|
-
}
|
|
302
|
-
globalThis.loadPyodide = loadPyodide;
|