pyodide 0.19.1 → 0.20.1-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/package.json +38 -9
- package/pyodide.ts +301 -0
- package/pyodide.umd.ts +3 -0
- package/api.js +0 -421
- package/error_handling.gen.ts +0 -294
- package/index.d.ts +0 -1
- package/index.test-d.ts +0 -162
- package/load-pyodide.js +0 -426
- package/module.js +0 -109
- package/pyodide.js +0 -302
- package/pyproxy.gen.ts +0 -1418
- package/rollup.config.js +0 -31
- package/test/conftest.js +0 -7
- package/test/filesystem.test.js +0 -12
- package/test/module.test.js +0 -10
- package/test/pyodide.test.mjs +0 -26
- package/tsconfig.json +0 -13
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pyodide",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.20.1-alpha.1",
|
|
4
4
|
"description": "The Pyodide JavaScript package",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"python",
|
|
@@ -16,22 +16,50 @@
|
|
|
16
16
|
},
|
|
17
17
|
"license": "Apache-2.0",
|
|
18
18
|
"devDependencies": {
|
|
19
|
+
"@rollup/plugin-commonjs": "^21.0.1",
|
|
20
|
+
"@rollup/plugin-node-resolve": "^13.1.3",
|
|
21
|
+
"@types/assert": "^1.5.6",
|
|
22
|
+
"@types/emscripten": "^1.39.5",
|
|
23
|
+
"@types/expect": "^24.3.0",
|
|
24
|
+
"@types/mocha": "^9.1.0",
|
|
25
|
+
"@types/node": "^17.0.25",
|
|
26
|
+
"@types/node-fetch": "^2.6.1",
|
|
27
|
+
"@types/ws": "^8.5.3",
|
|
28
|
+
"dts-bundle-generator": "^6.7.0",
|
|
29
|
+
"error-stack-parser": "^2.0.6",
|
|
30
|
+
"mocha": "^9.0.2",
|
|
19
31
|
"prettier": "^2.2.1",
|
|
20
|
-
"terser": "^5.7.0",
|
|
21
32
|
"rollup": "^2.48.0",
|
|
22
33
|
"rollup-plugin-terser": "^7.0.2",
|
|
34
|
+
"rollup-plugin-ts": "^2.0.5",
|
|
35
|
+
"terser": "^5.7.0",
|
|
36
|
+
"ts-mocha": "^9.0.2",
|
|
23
37
|
"tsd": "^0.15.1",
|
|
24
|
-
"
|
|
25
|
-
"
|
|
26
|
-
|
|
38
|
+
"typedoc": "^0.22.11",
|
|
39
|
+
"typescript": "^4.2.4"
|
|
40
|
+
},
|
|
41
|
+
"main": "pyodide.js",
|
|
42
|
+
"exports": {
|
|
43
|
+
".": {
|
|
44
|
+
"require": "./pyodide.js",
|
|
45
|
+
"import": "./pyodide.mjs"
|
|
46
|
+
},
|
|
47
|
+
"./pyodide.mjs": "./pyodide.mjs",
|
|
48
|
+
"./pyodide.js": "./pyodide.js",
|
|
49
|
+
"./package.json": "./package.json"
|
|
27
50
|
},
|
|
28
|
-
"
|
|
51
|
+
"files": [
|
|
52
|
+
"pyodide*",
|
|
53
|
+
"packages.json",
|
|
54
|
+
"distutils.tar",
|
|
55
|
+
"console.html"
|
|
56
|
+
],
|
|
29
57
|
"scripts": {
|
|
30
|
-
"test": "mocha"
|
|
58
|
+
"test": "ts-mocha -p tsconfig.test.json test/**/*.test.ts"
|
|
31
59
|
},
|
|
32
60
|
"mocha": {
|
|
33
61
|
"timeout": 30000,
|
|
34
|
-
"file": "
|
|
62
|
+
"file": "test/conftest.ts"
|
|
35
63
|
},
|
|
36
64
|
"tsd": {
|
|
37
65
|
"compilerOptions": {
|
|
@@ -43,6 +71,7 @@
|
|
|
43
71
|
},
|
|
44
72
|
"dependencies": {
|
|
45
73
|
"base-64": "^1.0.0",
|
|
46
|
-
"node-fetch": "^2.6.1"
|
|
74
|
+
"node-fetch": "^2.6.1",
|
|
75
|
+
"ws": "^8.5.0"
|
|
47
76
|
}
|
|
48
77
|
}
|
package/pyodide.ts
ADDED
|
@@ -0,0 +1,301 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The main bootstrap code for loading pyodide.
|
|
3
|
+
*/
|
|
4
|
+
import ErrorStackParser from "error-stack-parser";
|
|
5
|
+
import { loadScript, _loadBinaryFile, initNodeModules } from "./compat";
|
|
6
|
+
|
|
7
|
+
import { createModule, setStandardStreams, setHomeDirectory } from "./module";
|
|
8
|
+
|
|
9
|
+
import type { PyodideInterface } from "./api.js";
|
|
10
|
+
import type { PyProxy, PyProxyDict } from "./pyproxy.gen";
|
|
11
|
+
|
|
12
|
+
export type {
|
|
13
|
+
PyProxy,
|
|
14
|
+
PyProxyWithLength,
|
|
15
|
+
PyProxyDict,
|
|
16
|
+
PyProxyWithGet,
|
|
17
|
+
PyProxyWithSet,
|
|
18
|
+
PyProxyWithHas,
|
|
19
|
+
PyProxyIterable,
|
|
20
|
+
PyProxyIterator,
|
|
21
|
+
PyProxyAwaitable,
|
|
22
|
+
PyProxyBuffer,
|
|
23
|
+
PyProxyCallable,
|
|
24
|
+
TypedArray,
|
|
25
|
+
PyBuffer,
|
|
26
|
+
} from "./pyproxy.gen";
|
|
27
|
+
|
|
28
|
+
export type Py2JsResult = any;
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* A proxy around globals that falls back to checking for a builtin if has or
|
|
32
|
+
* get fails to find a global with the given key. Note that this proxy is
|
|
33
|
+
* transparent to js2python: it won't notice that this wrapper exists at all and
|
|
34
|
+
* will translate this proxy to the globals dictionary.
|
|
35
|
+
* @private
|
|
36
|
+
*/
|
|
37
|
+
function wrapPythonGlobals(
|
|
38
|
+
globals_dict: PyProxyDict,
|
|
39
|
+
builtins_dict: PyProxyDict
|
|
40
|
+
) {
|
|
41
|
+
return new Proxy(globals_dict, {
|
|
42
|
+
get(target, symbol) {
|
|
43
|
+
if (symbol === "get") {
|
|
44
|
+
return (key: any) => {
|
|
45
|
+
let result = target.get(key);
|
|
46
|
+
if (result === undefined) {
|
|
47
|
+
result = builtins_dict.get(key);
|
|
48
|
+
}
|
|
49
|
+
return result;
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
if (symbol === "has") {
|
|
53
|
+
return (key: any) => target.has(key) || builtins_dict.has(key);
|
|
54
|
+
}
|
|
55
|
+
return Reflect.get(target, symbol);
|
|
56
|
+
},
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function unpackPyodidePy(Module: any, pyodide_py_tar: Uint8Array) {
|
|
61
|
+
const fileName = "/pyodide_py.tar";
|
|
62
|
+
let stream = Module.FS.open(fileName, "w");
|
|
63
|
+
Module.FS.write(
|
|
64
|
+
stream,
|
|
65
|
+
pyodide_py_tar,
|
|
66
|
+
0,
|
|
67
|
+
pyodide_py_tar.byteLength,
|
|
68
|
+
undefined,
|
|
69
|
+
true
|
|
70
|
+
);
|
|
71
|
+
Module.FS.close(stream);
|
|
72
|
+
const code_ptr = Module.stringToNewUTF8(`
|
|
73
|
+
from sys import version_info
|
|
74
|
+
pyversion = f"python{version_info.major}.{version_info.minor}"
|
|
75
|
+
import shutil
|
|
76
|
+
shutil.unpack_archive("/pyodide_py.tar", f"/lib/{pyversion}/site-packages/")
|
|
77
|
+
del shutil
|
|
78
|
+
import importlib
|
|
79
|
+
importlib.invalidate_caches()
|
|
80
|
+
del importlib
|
|
81
|
+
`);
|
|
82
|
+
let errcode = Module._PyRun_SimpleString(code_ptr);
|
|
83
|
+
if (errcode) {
|
|
84
|
+
throw new Error("OOPS!");
|
|
85
|
+
}
|
|
86
|
+
Module._free(code_ptr);
|
|
87
|
+
Module.FS.unlink(fileName);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* This function is called after the emscripten module is finished initializing,
|
|
92
|
+
* so eval_code is newly available.
|
|
93
|
+
* It finishes the bootstrap so that once it is complete, it is possible to use
|
|
94
|
+
* the core `pyodide` apis. (But package loading is not ready quite yet.)
|
|
95
|
+
* @private
|
|
96
|
+
*/
|
|
97
|
+
function finalizeBootstrap(API: any, config: ConfigType) {
|
|
98
|
+
// First make internal dict so that we can use runPythonInternal.
|
|
99
|
+
// runPythonInternal uses a separate namespace, so we don't pollute the main
|
|
100
|
+
// environment with variables from our setup.
|
|
101
|
+
API.runPythonInternal_dict = API._pyodide._base.eval_code("{}") as PyProxy;
|
|
102
|
+
API.importlib = API.runPythonInternal("import importlib; importlib");
|
|
103
|
+
let import_module = API.importlib.import_module;
|
|
104
|
+
|
|
105
|
+
API.sys = import_module("sys");
|
|
106
|
+
API.sys.path.insert(0, config.homedir);
|
|
107
|
+
|
|
108
|
+
// Set up globals
|
|
109
|
+
let globals = API.runPythonInternal(
|
|
110
|
+
"import __main__; __main__.__dict__"
|
|
111
|
+
) as PyProxyDict;
|
|
112
|
+
let builtins = API.runPythonInternal(
|
|
113
|
+
"import builtins; builtins.__dict__"
|
|
114
|
+
) as PyProxyDict;
|
|
115
|
+
API.globals = wrapPythonGlobals(globals, builtins);
|
|
116
|
+
|
|
117
|
+
// Set up key Javascript modules.
|
|
118
|
+
let importhook = API._pyodide._importhook;
|
|
119
|
+
importhook.register_js_finder();
|
|
120
|
+
importhook.register_js_module("js", config.jsglobals);
|
|
121
|
+
|
|
122
|
+
let pyodide = API.makePublicAPI();
|
|
123
|
+
importhook.register_js_module("pyodide_js", pyodide);
|
|
124
|
+
|
|
125
|
+
// import pyodide_py. We want to ensure that as much stuff as possible is
|
|
126
|
+
// already set up before importing pyodide_py to simplify development of
|
|
127
|
+
// pyodide_py code (Otherwise it's very hard to keep track of which things
|
|
128
|
+
// aren't set up yet.)
|
|
129
|
+
API.pyodide_py = import_module("pyodide");
|
|
130
|
+
API.package_loader = import_module("pyodide._package_loader");
|
|
131
|
+
API.version = API.pyodide_py.__version__;
|
|
132
|
+
|
|
133
|
+
// copy some last constants onto public API.
|
|
134
|
+
pyodide.pyodide_py = API.pyodide_py;
|
|
135
|
+
pyodide.version = API.version;
|
|
136
|
+
pyodide.globals = API.globals;
|
|
137
|
+
return pyodide;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
declare function _createPyodideModule(Module: any): Promise<void>;
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* If indexURL isn't provided, throw an error and catch it and then parse our
|
|
144
|
+
* file name out from the stack trace.
|
|
145
|
+
*
|
|
146
|
+
* Question: But getting the URL from error stack trace is well... really
|
|
147
|
+
* hacky. Can't we use
|
|
148
|
+
* [`document.currentScript`](https://developer.mozilla.org/en-US/docs/Web/API/Document/currentScript)
|
|
149
|
+
* or
|
|
150
|
+
* [`import.meta.url`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/import.meta)
|
|
151
|
+
* instead?
|
|
152
|
+
*
|
|
153
|
+
* Answer: `document.currentScript` works for the browser main thread.
|
|
154
|
+
* `import.meta` works for es6 modules. In a classic webworker, I think there
|
|
155
|
+
* is no approach that works. Also we would need some third approach for node
|
|
156
|
+
* when loading a commonjs module using `require`. On the other hand, this
|
|
157
|
+
* stack trace approach works for every case without any feature detection
|
|
158
|
+
* code.
|
|
159
|
+
*/
|
|
160
|
+
function calculateIndexURL(): string {
|
|
161
|
+
let err;
|
|
162
|
+
try {
|
|
163
|
+
throw new Error();
|
|
164
|
+
} catch (e) {
|
|
165
|
+
err = e;
|
|
166
|
+
}
|
|
167
|
+
let fileName = ErrorStackParser.parse(err)[0].fileName!;
|
|
168
|
+
return fileName.slice(0, fileName.lastIndexOf("/"));
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
/**
|
|
172
|
+
* See documentation for loadPyodide.
|
|
173
|
+
* @private
|
|
174
|
+
*/
|
|
175
|
+
export type ConfigType = {
|
|
176
|
+
indexURL: string;
|
|
177
|
+
homedir: string;
|
|
178
|
+
fullStdLib?: boolean;
|
|
179
|
+
stdin?: () => string;
|
|
180
|
+
stdout?: (msg: string) => void;
|
|
181
|
+
stderr?: (msg: string) => void;
|
|
182
|
+
jsglobals?: object;
|
|
183
|
+
};
|
|
184
|
+
|
|
185
|
+
/**
|
|
186
|
+
* Load the main Pyodide wasm module and initialize it.
|
|
187
|
+
*
|
|
188
|
+
* Only one copy of Pyodide can be loaded in a given JavaScript global scope
|
|
189
|
+
* because Pyodide uses global variables to load packages. If an attempt is made
|
|
190
|
+
* to load a second copy of Pyodide, :any:`loadPyodide` will throw an error.
|
|
191
|
+
* (This can be fixed once `Firefox adopts support for ES6 modules in webworkers
|
|
192
|
+
* <https://bugzilla.mozilla.org/show_bug.cgi?id=1247687>`_.)
|
|
193
|
+
*
|
|
194
|
+
* @returns The :ref:`js-api-pyodide` module.
|
|
195
|
+
* @memberof globalThis
|
|
196
|
+
* @async
|
|
197
|
+
*/
|
|
198
|
+
export async function loadPyodide(
|
|
199
|
+
options: {
|
|
200
|
+
/**
|
|
201
|
+
* The URL from which Pyodide will load the main Pyodide runtime and
|
|
202
|
+
* packages. Defaults to the url that pyodide is loaded from with the file
|
|
203
|
+
* name (pyodide.js or pyodide.mjs) removed. It is recommended that you
|
|
204
|
+
* leave this undefined, providing an incorrect value can cause broken
|
|
205
|
+
* behavior.
|
|
206
|
+
*/
|
|
207
|
+
indexURL?: string;
|
|
208
|
+
|
|
209
|
+
/**
|
|
210
|
+
* The home directory which Pyodide will use inside virtual file system. Default: "/home/pyodide"
|
|
211
|
+
*/
|
|
212
|
+
homedir?: string;
|
|
213
|
+
|
|
214
|
+
/** Load the full Python standard library.
|
|
215
|
+
* Setting this to false excludes following modules: distutils.
|
|
216
|
+
* Default: true
|
|
217
|
+
*/
|
|
218
|
+
fullStdLib?: boolean;
|
|
219
|
+
/**
|
|
220
|
+
* Override the standard input callback. Should ask the user for one line of input.
|
|
221
|
+
*/
|
|
222
|
+
stdin?: () => string;
|
|
223
|
+
/**
|
|
224
|
+
* Override the standard output callback.
|
|
225
|
+
* Default: undefined
|
|
226
|
+
*/
|
|
227
|
+
stdout?: (msg: string) => void;
|
|
228
|
+
/**
|
|
229
|
+
* Override the standard error output callback.
|
|
230
|
+
* Default: undefined
|
|
231
|
+
*/
|
|
232
|
+
stderr?: (msg: string) => void;
|
|
233
|
+
jsglobals?: object;
|
|
234
|
+
} = {}
|
|
235
|
+
): Promise<PyodideInterface> {
|
|
236
|
+
if (!options.indexURL) {
|
|
237
|
+
options.indexURL = calculateIndexURL();
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
const default_config = {
|
|
241
|
+
fullStdLib: true,
|
|
242
|
+
jsglobals: globalThis,
|
|
243
|
+
stdin: globalThis.prompt ? globalThis.prompt : undefined,
|
|
244
|
+
homedir: "/home/pyodide",
|
|
245
|
+
};
|
|
246
|
+
const config = Object.assign(default_config, options) as ConfigType;
|
|
247
|
+
if (!config.indexURL.endsWith("/")) {
|
|
248
|
+
config.indexURL += "/";
|
|
249
|
+
}
|
|
250
|
+
await initNodeModules();
|
|
251
|
+
const pyodide_py_tar_promise = _loadBinaryFile(
|
|
252
|
+
config.indexURL,
|
|
253
|
+
"pyodide_py.tar"
|
|
254
|
+
);
|
|
255
|
+
|
|
256
|
+
const Module = createModule();
|
|
257
|
+
const API: any = { config };
|
|
258
|
+
Module.API = API;
|
|
259
|
+
|
|
260
|
+
setStandardStreams(Module, config.stdin, config.stdout, config.stderr);
|
|
261
|
+
setHomeDirectory(Module, config.homedir);
|
|
262
|
+
|
|
263
|
+
const moduleLoaded = new Promise((r) => (Module.postRun = r));
|
|
264
|
+
|
|
265
|
+
// locateFile tells Emscripten where to find the data files that initialize
|
|
266
|
+
// the file system.
|
|
267
|
+
Module.locateFile = (path: string) => config.indexURL + path;
|
|
268
|
+
const scriptSrc = `${config.indexURL}pyodide.asm.js`;
|
|
269
|
+
await loadScript(scriptSrc);
|
|
270
|
+
|
|
271
|
+
// _createPyodideModule is specified in the Makefile by the linker flag:
|
|
272
|
+
// `-s EXPORT_NAME="'_createPyodideModule'"`
|
|
273
|
+
await _createPyodideModule(Module);
|
|
274
|
+
|
|
275
|
+
// There is some work to be done between the module being "ready" and postRun
|
|
276
|
+
// being called.
|
|
277
|
+
await moduleLoaded;
|
|
278
|
+
|
|
279
|
+
// Disable further loading of Emscripten file_packager stuff.
|
|
280
|
+
Module.locateFile = (path: string) => {
|
|
281
|
+
throw new Error("Didn't expect to load any more file_packager files!");
|
|
282
|
+
};
|
|
283
|
+
|
|
284
|
+
const pyodide_py_tar = await pyodide_py_tar_promise;
|
|
285
|
+
unpackPyodidePy(Module, pyodide_py_tar);
|
|
286
|
+
Module._pyodide_init();
|
|
287
|
+
|
|
288
|
+
const pyodide = finalizeBootstrap(API, config);
|
|
289
|
+
// API.runPython works starting here.
|
|
290
|
+
if (!pyodide.version.includes("dev")) {
|
|
291
|
+
// Currently only used in Node to download packages the first time they are
|
|
292
|
+
// loaded. But in other cases it's harmless.
|
|
293
|
+
API.setCdnUrl(`https://cdn.jsdelivr.net/pyodide/v${pyodide.version}/full/`);
|
|
294
|
+
}
|
|
295
|
+
await API.packageIndexReady;
|
|
296
|
+
if (config.fullStdLib) {
|
|
297
|
+
await pyodide.loadPackage(["distutils"]);
|
|
298
|
+
}
|
|
299
|
+
pyodide.runPython("print('Python initialization complete')");
|
|
300
|
+
return pyodide;
|
|
301
|
+
}
|
package/pyodide.umd.ts
ADDED