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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pyodide",
3
- "version": "0.19.1",
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
- "typescript": "^4.2.4",
25
- "mocha": "^9.0.2",
26
- "@types/emscripten": "^1.39.5"
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
- "type": "module",
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": "./test/conftest.js"
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
@@ -0,0 +1,3 @@
1
+ import { loadPyodide } from "./pyodide";
2
+ export { loadPyodide };
3
+ (globalThis as any).loadPyodide = loadPyodide;