voting_schemes-electionguard 0.24.2 → 0.24.4
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.rubocop.yml +1 -1
- data/Gemfile.lock +100 -68
- data/app/assets/javascripts/voting_schemes/electionguard/electionguard.js +1 -1
- data/lib/voting_schemes/electionguard/version.rb +1 -1
- data/public/assets/electionguard/attrs.data +0 -0
- data/public/assets/electionguard/attrs.js +1 -1
- data/public/assets/electionguard/bulletin_board-electionguard.data +0 -0
- data/public/assets/electionguard/bulletin_board-electionguard.js +1 -1
- data/public/assets/electionguard/console.html +89 -61
- data/public/assets/electionguard/electionguard.js +1 -1
- data/public/assets/electionguard/gmpy2.data +0 -0
- data/public/assets/electionguard/gmpy2.js +1 -1
- data/public/assets/electionguard/hypothesis.js +1 -1
- data/public/assets/electionguard/jsons.js +1 -1
- data/public/assets/electionguard/micropip.data +0 -0
- data/public/assets/electionguard/micropip.js +1 -1
- data/public/assets/electionguard/packages.json +1 -1
- data/public/assets/electionguard/packaging.data +0 -0
- data/public/assets/electionguard/packaging.js +1 -0
- data/public/assets/electionguard/pyasn1.js +1 -1
- data/public/assets/electionguard/pyodide.asm.data +0 -0
- data/public/assets/electionguard/pyodide.asm.js +11 -11
- data/public/assets/electionguard/pyodide.asm.wasm +0 -0
- data/public/assets/electionguard/pyodide.js +781 -333
- data/public/assets/electionguard/pyparsing.data +0 -0
- data/public/assets/electionguard/pyparsing.js +1 -0
- data/public/assets/electionguard/rsa.js +1 -1
- data/public/assets/electionguard/six.data +0 -0
- data/public/assets/electionguard/six.js +1 -0
- data/public/assets/electionguard/sortedcontainers.js +1 -1
- data/public/assets/electionguard/test.data +0 -0
- data/public/assets/electionguard/test.html +21 -24
- data/public/assets/electionguard/test.js +1 -1
- data/public/assets/electionguard/typish.js +1 -1
- data/public/assets/electionguard/webworker.js +29 -21
- data/public/assets/electionguard/webworker_dev.js +14 -14
- metadata +8 -6
- data/public/assets/electionguard/distlib.data +0 -0
- data/public/assets/electionguard/distlib.js +0 -1
- data/public/assets/electionguard/pyodide.asm.data.js +0 -1
- data/public/assets/electionguard/renderedhtml.css +0 -209
@@ -2,270 +2,366 @@
|
|
2
2
|
* The main bootstrap script for loading pyodide.
|
3
3
|
*/
|
4
4
|
|
5
|
-
|
6
|
-
|
5
|
+
/**
|
6
|
+
* The :ref:`js-api-pyodide` module object. Must be present as a global variable
|
7
|
+
* called
|
8
|
+
* ``pyodide`` in order for package loading to work properly.
|
9
|
+
*
|
10
|
+
* @type Object
|
11
|
+
*/
|
12
|
+
globalThis.pyodide = {};
|
13
|
+
|
14
|
+
/**
|
15
|
+
* Load the main Pyodide wasm module and initialize it. When finished stores the
|
16
|
+
* Pyodide module as a global object called ``pyodide``.
|
17
|
+
* @param {string} config.indexURL - The URL from which Pyodide will load
|
18
|
+
* packages
|
19
|
+
* @returns The Pyodide module.
|
20
|
+
* @async
|
21
|
+
*/
|
22
|
+
globalThis.loadPyodide = async function(config = {}) {
|
23
|
+
if (globalThis.__pyodideLoading) {
|
24
|
+
if (globalThis.languagePluginURL) {
|
25
|
+
throw new Error(
|
26
|
+
"Pyodide is already loading because languagePluginURL is defined.");
|
27
|
+
} else {
|
28
|
+
throw new Error("Pyodide is already loading.");
|
29
|
+
}
|
30
|
+
}
|
31
|
+
globalThis.__pyodideLoading = true;
|
32
|
+
let Module = {};
|
33
|
+
// Note: PYODIDE_BASE_URL is an environment variable replaced in
|
7
34
|
// in this template in the Makefile. It's recommended to always set
|
8
|
-
//
|
9
|
-
|
10
|
-
|
35
|
+
// indexURL in any case.
|
36
|
+
let baseURL = config.indexURL || "./";
|
37
|
+
if (baseURL.endsWith(".js")) {
|
38
|
+
baseURL = baseURL.substr(0, baseURL.lastIndexOf('/'));
|
39
|
+
}
|
40
|
+
if (!baseURL.endsWith("/")) {
|
41
|
+
baseURL += '/';
|
42
|
+
}
|
11
43
|
|
12
44
|
////////////////////////////////////////////////////////////
|
13
45
|
// Package loading
|
14
|
-
|
15
|
-
|
46
|
+
const DEFAULT_CHANNEL = "default channel";
|
47
|
+
|
16
48
|
// Regexp for validating package name and URI
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
let _uri_to_package_name = (package_uri) => {
|
23
|
-
// Generate a unique package name from URI
|
24
|
-
|
25
|
-
if (package_name_regexp.test(package_uri)) {
|
26
|
-
return package_uri;
|
27
|
-
} else if (package_uri_regexp.test(package_uri)) {
|
28
|
-
let match = package_uri_regexp.exec(package_uri);
|
29
|
-
// Get the regexp group corresponding to the package name
|
49
|
+
const package_uri_regexp = /^.*?([^\/]*)\.js$/;
|
50
|
+
|
51
|
+
function _uri_to_package_name(package_uri) {
|
52
|
+
let match = package_uri_regexp.exec(package_uri);
|
53
|
+
if (match) {
|
30
54
|
return match[1];
|
31
|
-
} else {
|
32
|
-
return null;
|
33
55
|
}
|
34
56
|
};
|
35
57
|
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
// can't be done synchronously within the call to dlopen, we instantiate
|
40
|
-
// every .so that comes our way up front, caching it in the
|
41
|
-
// `preloadedWasm` dictionary.
|
42
|
-
|
43
|
-
let promise = new Promise((resolve) => resolve());
|
44
|
-
let FS = pyodide._module.FS;
|
45
|
-
|
46
|
-
function recurseDir(rootpath) {
|
47
|
-
let dirs;
|
48
|
-
try {
|
49
|
-
dirs = FS.readdir(rootpath);
|
50
|
-
} catch {
|
51
|
-
return;
|
52
|
-
}
|
53
|
-
for (let entry of dirs) {
|
54
|
-
if (entry.startsWith('.')) {
|
55
|
-
continue;
|
56
|
-
}
|
57
|
-
const path = rootpath + entry;
|
58
|
-
if (entry.endsWith('.so')) {
|
59
|
-
if (Module['preloadedWasm'][path] === undefined) {
|
60
|
-
promise = promise
|
61
|
-
.then(() => Module['loadWebAssemblyModule'](
|
62
|
-
FS.readFile(path), {loadAsync: true}))
|
63
|
-
.then((module) => {
|
64
|
-
Module['preloadedWasm'][path] = module;
|
65
|
-
});
|
66
|
-
}
|
67
|
-
} else if (FS.isDir(FS.lookupPath(path).node.mode)) {
|
68
|
-
recurseDir(path + '/');
|
69
|
-
}
|
70
|
-
}
|
71
|
-
}
|
72
|
-
|
73
|
-
recurseDir('/');
|
74
|
-
|
75
|
-
return promise;
|
76
|
-
}
|
77
|
-
// clang-format on
|
78
|
-
|
79
|
-
function loadScript(url, onload, onerror) {
|
80
|
-
if (self.document) { // browser
|
58
|
+
let loadScript;
|
59
|
+
if (self.document) { // browser
|
60
|
+
loadScript = (url) => new Promise((res, rej) => {
|
81
61
|
const script = self.document.createElement('script');
|
82
62
|
script.src = url;
|
83
|
-
script.onload =
|
84
|
-
script.onerror =
|
63
|
+
script.onload = res;
|
64
|
+
script.onerror = rej;
|
85
65
|
self.document.head.appendChild(script);
|
86
|
-
}
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
} catch {
|
91
|
-
onerror();
|
92
|
-
}
|
93
|
-
}
|
94
|
-
}
|
95
|
-
|
96
|
-
let _loadPackage = (names, messageCallback, errorCallback) => {
|
97
|
-
if (messageCallback == undefined) {
|
98
|
-
messageCallback = () => {};
|
99
|
-
}
|
100
|
-
if (errorCallback == undefined) {
|
101
|
-
errorCallback = () => {};
|
102
|
-
}
|
103
|
-
let _messageCallback = (msg) => {
|
104
|
-
console.log(msg);
|
105
|
-
messageCallback(msg);
|
106
|
-
};
|
107
|
-
let _errorCallback = (errMsg) => {
|
108
|
-
console.error(errMsg);
|
109
|
-
errorCallback(errMsg);
|
66
|
+
});
|
67
|
+
} else if (self.importScripts) { // webworker
|
68
|
+
loadScript = async (url) => { // This is async only for consistency
|
69
|
+
self.importScripts(url);
|
110
70
|
};
|
71
|
+
} else {
|
72
|
+
throw new Error("Cannot determine runtime environment");
|
73
|
+
}
|
111
74
|
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
let package_uri = queue.pop();
|
119
|
-
|
120
|
-
const pkg = _uri_to_package_name(package_uri);
|
75
|
+
function recursiveDependencies(names, _messageCallback, errorCallback,
|
76
|
+
sharedLibsOnly) {
|
77
|
+
const packages = Module.packages.dependencies;
|
78
|
+
const loadedPackages = Module.loadedPackages;
|
79
|
+
const sharedLibraries = Module.packages.shared_library;
|
80
|
+
const toLoad = new Map();
|
121
81
|
|
122
|
-
|
123
|
-
|
82
|
+
const addPackage = (pkg) => {
|
83
|
+
if (toLoad.has(pkg)) {
|
124
84
|
return;
|
125
|
-
} else if (pkg == package_uri) {
|
126
|
-
package_uri = 'default channel';
|
127
85
|
}
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
86
|
+
toLoad.set(pkg, DEFAULT_CHANNEL);
|
87
|
+
// If the package is already loaded, we don't add dependencies, but warn
|
88
|
+
// the user later. This is especially important if the loaded package is
|
89
|
+
// from a custom url, in which case adding dependencies is wrong.
|
90
|
+
if (loadedPackages[pkg] !== undefined) {
|
91
|
+
return;
|
92
|
+
}
|
93
|
+
for (let dep of packages[pkg]) {
|
94
|
+
addPackage(dep);
|
95
|
+
}
|
96
|
+
};
|
97
|
+
for (let name of names) {
|
98
|
+
const pkgname = _uri_to_package_name(name);
|
99
|
+
if (pkgname !== undefined) {
|
100
|
+
if (toLoad.has(pkgname) && toLoad.get(pkgname) !== name) {
|
101
|
+
errorCallback(`Loading same package ${pkgname} from ${name} and ${
|
102
|
+
toLoad.get(pkgname)}`);
|
103
|
+
continue;
|
144
104
|
}
|
105
|
+
toLoad.set(pkgname, name);
|
106
|
+
} else if (name in packages) {
|
107
|
+
addPackage(name);
|
145
108
|
} else {
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
}
|
155
|
-
});
|
156
|
-
} else {
|
157
|
-
_errorCallback(`Unknown package '${pkg}'`);
|
109
|
+
errorCallback(`Skipping unknown package '${name}'`);
|
110
|
+
}
|
111
|
+
}
|
112
|
+
if (sharedLibsOnly) {
|
113
|
+
let onlySharedLibs = new Map();
|
114
|
+
for (let c of toLoad) {
|
115
|
+
if (c[0] in sharedLibraries) {
|
116
|
+
onlySharedLibs.set(c[0], toLoad.get(c[0]));
|
158
117
|
}
|
159
118
|
}
|
119
|
+
return onlySharedLibs;
|
160
120
|
}
|
121
|
+
return toLoad;
|
122
|
+
}
|
161
123
|
|
162
|
-
|
124
|
+
async function _loadPackage(names, messageCallback, errorCallback) {
|
125
|
+
// toLoad is a map pkg_name => pkg_uri
|
126
|
+
let toLoad = recursiveDependencies(names, messageCallback, errorCallback);
|
127
|
+
|
128
|
+
// locateFile is the function used by the .js file to locate the .data
|
129
|
+
// file given the filename
|
130
|
+
Module.locateFile = (path) => {
|
163
131
|
// handle packages loaded from custom URLs
|
164
132
|
let pkg = path.replace(/\.data$/, "");
|
165
|
-
if (pkg
|
166
|
-
let package_uri = toLoad
|
167
|
-
if (package_uri !=
|
133
|
+
if (toLoad.has(pkg)) {
|
134
|
+
let package_uri = toLoad.get(pkg);
|
135
|
+
if (package_uri != DEFAULT_CHANNEL) {
|
168
136
|
return package_uri.replace(/\.js$/, ".data");
|
169
137
|
};
|
170
138
|
};
|
171
139
|
return baseURL + path;
|
172
140
|
};
|
173
141
|
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
}
|
179
|
-
|
180
|
-
let packageList = Array.from(Object.keys(toLoad));
|
181
|
-
_messageCallback(`Loading ${packageList.join(', ')}`)
|
142
|
+
if (toLoad.size === 0) {
|
143
|
+
return Promise.resolve('No new packages to load');
|
144
|
+
} else {
|
145
|
+
let packageNames = Array.from(toLoad.keys()).join(', ');
|
146
|
+
messageCallback(`Loading ${packageNames}`);
|
147
|
+
}
|
182
148
|
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
149
|
+
// If running in main browser thread, try to catch errors thrown when
|
150
|
+
// running a script. Since the script is added via a script tag, there is
|
151
|
+
// no good way to capture errors from the script only, so try to capture
|
152
|
+
// all errors them.
|
153
|
+
//
|
154
|
+
// windowErrorPromise rejects when any exceptions is thrown in the process
|
155
|
+
// of loading a script. The promise never resolves, and we combine it
|
156
|
+
// with other promises via Promise.race.
|
157
|
+
let windowErrorHandler;
|
158
|
+
let windowErrorPromise;
|
159
|
+
if (self.document) {
|
160
|
+
windowErrorPromise = new Promise((_res, rej) => {
|
161
|
+
windowErrorHandler = e => {
|
162
|
+
errorCallback(
|
163
|
+
"Unhandled error. We don't know what it is or whether it is related to 'loadPackage' but out of an abundance of caution we will assume that loading failed.");
|
164
|
+
errorCallback(e);
|
165
|
+
rej(e.message);
|
166
|
+
};
|
167
|
+
self.addEventListener('error', windowErrorHandler);
|
168
|
+
});
|
169
|
+
} else {
|
170
|
+
// This should be a promise that never resolves
|
171
|
+
windowErrorPromise = new Promise(() => {});
|
172
|
+
}
|
187
173
|
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
174
|
+
// This is a collection of promises that resolve when the package's JS file
|
175
|
+
// is loaded. The promises already handle error and never fail.
|
176
|
+
let scriptPromises = [];
|
177
|
+
|
178
|
+
for (let [pkg, uri] of toLoad) {
|
179
|
+
let loaded = Module.loadedPackages[pkg];
|
180
|
+
if (loaded !== undefined) {
|
181
|
+
// If uri is from the DEFAULT_CHANNEL, we assume it was added as a
|
182
|
+
// depedency, which was previously overridden.
|
183
|
+
if (loaded === uri || uri === DEFAULT_CHANNEL) {
|
184
|
+
messageCallback(`${pkg} already loaded from ${loaded}`);
|
185
|
+
continue;
|
186
|
+
} else {
|
187
|
+
errorCallback(
|
188
|
+
`URI mismatch, attempting to load package ${pkg} from ${uri} ` +
|
189
|
+
`while it is already loaded from ${
|
190
|
+
loaded}. To override a dependency, ` +
|
191
|
+
`load the custom package first.`);
|
192
|
+
continue;
|
193
|
+
}
|
194
|
+
}
|
195
|
+
let scriptSrc = uri === DEFAULT_CHANNEL ? `${baseURL}${pkg}.js` : uri;
|
196
|
+
messageCallback(`Loading ${pkg} from ${scriptSrc}`);
|
197
|
+
scriptPromises.push(loadScript(scriptSrc).catch(() => {
|
198
|
+
errorCallback(`Couldn't load package from URL ${scriptSrc}`);
|
199
|
+
toLoad.delete(pkg);
|
200
|
+
}));
|
201
|
+
}
|
203
202
|
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
203
|
+
// When the JS loads, it synchronously adds a runDependency to emscripten.
|
204
|
+
// It then loads the data file, and removes the runDependency from
|
205
|
+
// emscripten. This function returns a promise that resolves when there are
|
206
|
+
// no pending runDependencies.
|
207
|
+
function waitRunDependency() {
|
208
|
+
const promise = new Promise(r => {
|
209
|
+
Module.monitorRunDependencies = (n) => {
|
210
|
+
if (n === 0) {
|
211
|
+
r();
|
212
212
|
}
|
213
|
-
}
|
214
|
-
};
|
213
|
+
};
|
214
|
+
});
|
215
|
+
// If there are no pending dependencies left, monitorRunDependencies will
|
216
|
+
// never be called. Since we can't check the number of dependencies,
|
217
|
+
// manually trigger a call.
|
218
|
+
Module.addRunDependency("dummy");
|
219
|
+
Module.removeRunDependency("dummy");
|
220
|
+
return promise;
|
221
|
+
}
|
215
222
|
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
223
|
+
// We must start waiting for runDependencies *after* all the JS files are
|
224
|
+
// loaded, since the number of runDependencies may happen to equal zero
|
225
|
+
// between package files loading.
|
226
|
+
let successPromise = Promise.all(scriptPromises).then(waitRunDependency);
|
227
|
+
try {
|
228
|
+
await Promise.race([ successPromise, windowErrorPromise ]);
|
229
|
+
} finally {
|
230
|
+
delete Module.monitorRunDependencies;
|
231
|
+
if (windowErrorHandler) {
|
220
232
|
self.removeEventListener('error', windowErrorHandler);
|
221
|
-
// Set up a new Promise chain, since this one failed
|
222
|
-
loadPackagePromise = new Promise((resolve) => resolve());
|
223
|
-
reject(err.message);
|
224
|
-
};
|
225
|
-
self.addEventListener('error', windowErrorHandler);
|
226
|
-
|
227
|
-
for (let pkg in toLoad) {
|
228
|
-
let scriptSrc;
|
229
|
-
let package_uri = toLoad[pkg];
|
230
|
-
if (package_uri == 'default channel') {
|
231
|
-
scriptSrc = `${baseURL}${pkg}.js`;
|
232
|
-
} else {
|
233
|
-
scriptSrc = `${package_uri}`;
|
234
|
-
}
|
235
|
-
_messageCallback(`Loading ${pkg} from ${scriptSrc}`)
|
236
|
-
loadScript(scriptSrc, () => {}, () => {
|
237
|
-
// If the package_uri fails to load, call monitorRunDependencies twice
|
238
|
-
// (so packageCounter will still hit 0 and finish loading), and remove
|
239
|
-
// the package from toLoad so we don't mark it as loaded, and remove
|
240
|
-
// the package from packageList so we don't say that it was loaded.
|
241
|
-
_errorCallback(`Couldn't load package from URL ${scriptSrc}`);
|
242
|
-
delete toLoad[pkg];
|
243
|
-
let packageListIndex = packageList.indexOf(pkg);
|
244
|
-
if (packageListIndex !== -1) {
|
245
|
-
packageList.splice(packageListIndex, 1);
|
246
|
-
}
|
247
|
-
for (let i = 0; i < 2; i++) {
|
248
|
-
self.pyodide._module.monitorRunDependencies();
|
249
|
-
}
|
250
|
-
});
|
251
233
|
}
|
234
|
+
}
|
252
235
|
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
|
236
|
+
let packageList = [];
|
237
|
+
for (let [pkg, uri] of toLoad) {
|
238
|
+
Module.loadedPackages[pkg] = uri;
|
239
|
+
packageList.push(pkg);
|
240
|
+
}
|
241
|
+
|
242
|
+
let resolveMsg;
|
243
|
+
if (packageList.length > 0) {
|
244
|
+
let packageNames = packageList.join(', ');
|
245
|
+
resolveMsg = `Loaded ${packageNames}`;
|
246
|
+
} else {
|
247
|
+
resolveMsg = 'No packages loaded';
|
248
|
+
}
|
249
|
+
|
250
|
+
Module.reportUndefinedSymbols();
|
251
|
+
|
252
|
+
messageCallback(resolveMsg);
|
259
253
|
|
260
|
-
|
254
|
+
// We have to invalidate Python's import caches, or it won't
|
255
|
+
// see the new files.
|
256
|
+
Module.runPythonSimple('import importlib\n' +
|
257
|
+
'importlib.invalidate_caches()\n');
|
261
258
|
};
|
262
259
|
|
263
|
-
|
264
|
-
|
265
|
-
|
266
|
-
|
267
|
-
|
268
|
-
|
260
|
+
// This is a promise that is resolved iff there are no pending package loads.
|
261
|
+
// It never fails.
|
262
|
+
let loadPackageChain = Promise.resolve();
|
263
|
+
|
264
|
+
/**
|
265
|
+
*
|
266
|
+
* The list of packages that Pyodide has loaded.
|
267
|
+
* Use ``Object.keys(pyodide.loadedPackages)`` to get the list of names of
|
268
|
+
* loaded packages, and ``pyodide.loadedPackages[package_name]`` to access
|
269
|
+
* install location for a particular ``package_name``.
|
270
|
+
*
|
271
|
+
* @type {object}
|
272
|
+
*/
|
273
|
+
Module.loadedPackages = {};
|
274
|
+
|
275
|
+
/**
|
276
|
+
* Load a package or a list of packages over the network. This installs the
|
277
|
+
* package in the virtual filesystem. The package needs to be imported from
|
278
|
+
* Python before it can be used.
|
279
|
+
* @param {String | Array | PyProxy} names Either a single package name or URL
|
280
|
+
* or a list of them. URLs can be absolute or relative. The URLs must have
|
281
|
+
* file name
|
282
|
+
* ``<package-name>.js`` and there must be a file called
|
283
|
+
* ``<package-name>.data`` in the same directory. The argument can be a
|
284
|
+
* ``PyProxy`` of a list, in which case the list will be converted to
|
285
|
+
* Javascript and the ``PyProxy`` will be destroyed.
|
286
|
+
* @param {function} messageCallback A callback, called with progress messages
|
287
|
+
* (optional)
|
288
|
+
* @param {function} errorCallback A callback, called with error/warning
|
289
|
+
* messages (optional)
|
290
|
+
* @async
|
291
|
+
*/
|
292
|
+
Module.loadPackage = async function(names, messageCallback, errorCallback) {
|
293
|
+
if (Module.isPyProxy(names)) {
|
294
|
+
let temp;
|
295
|
+
try {
|
296
|
+
temp = names.toJs();
|
297
|
+
} finally {
|
298
|
+
names.destroy();
|
299
|
+
}
|
300
|
+
names = temp;
|
301
|
+
}
|
302
|
+
|
303
|
+
if (!Array.isArray(names)) {
|
304
|
+
names = [ names ];
|
305
|
+
}
|
306
|
+
// get shared library packages and load those first
|
307
|
+
// otherwise bad things happen with linking them in firefox.
|
308
|
+
let sharedLibraryNames = [];
|
309
|
+
try {
|
310
|
+
let sharedLibraryPackagesToLoad =
|
311
|
+
recursiveDependencies(names, messageCallback, errorCallback, true);
|
312
|
+
for (let pkg of sharedLibraryPackagesToLoad) {
|
313
|
+
sharedLibraryNames.push(pkg[0]);
|
314
|
+
}
|
315
|
+
} catch (e) {
|
316
|
+
// do nothing - let the main load throw any errors
|
317
|
+
}
|
318
|
+
// override the load plugin so that it imports any dlls also
|
319
|
+
// this only needs to be done for shared library packages because
|
320
|
+
// we assume that if a package depends on a shared library
|
321
|
+
// it needs to have access to it.
|
322
|
+
// not needed for so in standard module because those are linked together
|
323
|
+
// correctly, it is only where linking goes across modules that it needs to
|
324
|
+
// be done. Hence we only put this extra preload plugin in during the shared
|
325
|
+
// library load
|
326
|
+
let oldPlugin;
|
327
|
+
for (let p in Module.preloadPlugins) {
|
328
|
+
if (Module.preloadPlugins[p].canHandle("test.so")) {
|
329
|
+
oldPlugin = Module.preloadPlugins[p];
|
330
|
+
break;
|
331
|
+
}
|
332
|
+
}
|
333
|
+
let dynamicLoadHandler = {
|
334
|
+
get : function(obj, prop) {
|
335
|
+
if (prop === 'handle') {
|
336
|
+
return function(bytes, name) {
|
337
|
+
obj[prop].apply(obj, arguments);
|
338
|
+
this["asyncWasmLoadPromise"] =
|
339
|
+
this["asyncWasmLoadPromise"].then(function() {
|
340
|
+
Module.loadDynamicLibrary(name,
|
341
|
+
{global : true, nodelete : true})
|
342
|
+
});
|
343
|
+
}
|
344
|
+
} else {
|
345
|
+
return obj[prop];
|
346
|
+
}
|
347
|
+
}
|
348
|
+
};
|
349
|
+
var loadPluginOverride = new Proxy(oldPlugin, dynamicLoadHandler);
|
350
|
+
// restore the preload plugin
|
351
|
+
Module.preloadPlugins.unshift(loadPluginOverride);
|
352
|
+
|
353
|
+
let promise = loadPackageChain.then(
|
354
|
+
() => _loadPackage(sharedLibraryNames, messageCallback || console.log,
|
355
|
+
errorCallback || console.error));
|
356
|
+
loadPackageChain = loadPackageChain.then(() => promise.catch(() => {}));
|
357
|
+
await promise;
|
358
|
+
Module.preloadPlugins.shift(loadPluginOverride);
|
359
|
+
|
360
|
+
promise = loadPackageChain.then(
|
361
|
+
() => _loadPackage(names, messageCallback || console.log,
|
362
|
+
errorCallback || console.error));
|
363
|
+
loadPackageChain = loadPackageChain.then(() => promise.catch(() => {}));
|
364
|
+
await promise;
|
269
365
|
};
|
270
366
|
|
271
367
|
////////////////////////////////////////////////////////////
|
@@ -292,27 +388,35 @@ var languagePluginLoader = new Promise((resolve, reject) => {
|
|
292
388
|
if (recursionLimit > 1000) {
|
293
389
|
recursionLimit = 1000;
|
294
390
|
}
|
295
|
-
pyodide.
|
391
|
+
pyodide.runPythonSimple(
|
296
392
|
`import sys; sys.setrecursionlimit(int(${recursionLimit}))`);
|
297
393
|
};
|
298
394
|
|
299
395
|
////////////////////////////////////////////////////////////
|
300
396
|
// Rearrange namespace for public API
|
397
|
+
// clang-format off
|
301
398
|
let PUBLIC_API = [
|
302
399
|
'globals',
|
400
|
+
'pyodide_py',
|
401
|
+
'version',
|
303
402
|
'loadPackage',
|
403
|
+
'loadPackagesFromImports',
|
304
404
|
'loadedPackages',
|
405
|
+
'isPyProxy',
|
305
406
|
'pyimport',
|
306
|
-
'repr',
|
307
407
|
'runPython',
|
308
408
|
'runPythonAsync',
|
309
|
-
'
|
310
|
-
'
|
311
|
-
'
|
409
|
+
'registerJsModule',
|
410
|
+
'unregisterJsModule',
|
411
|
+
'setInterruptBuffer',
|
412
|
+
'toPy',
|
413
|
+
'PythonError',
|
312
414
|
];
|
415
|
+
// clang-format on
|
313
416
|
|
314
417
|
function makePublicAPI(module, public_api) {
|
315
|
-
|
418
|
+
let namespace = {_module : module};
|
419
|
+
module.public_api = namespace;
|
316
420
|
for (let name of public_api) {
|
317
421
|
namespace[name] = module[name];
|
318
422
|
}
|
@@ -321,120 +425,464 @@ var languagePluginLoader = new Promise((resolve, reject) => {
|
|
321
425
|
|
322
426
|
////////////////////////////////////////////////////////////
|
323
427
|
// Loading Pyodide
|
324
|
-
let Module = {};
|
325
|
-
self.Module = Module;
|
326
428
|
|
327
429
|
Module.noImageDecoding = true;
|
328
430
|
Module.noAudioDecoding = true;
|
329
|
-
Module.noWasmDecoding =
|
431
|
+
Module.noWasmDecoding =
|
432
|
+
false; // we preload wasm using the built in plugin now
|
330
433
|
Module.preloadedWasm = {};
|
331
|
-
|
332
|
-
|
333
|
-
Module.
|
334
|
-
if (
|
335
|
-
|
336
|
-
|
337
|
-
|
338
|
-
console.error(ABI_mismatch_exception);
|
339
|
-
throw ABI_mismatch_exception;
|
434
|
+
|
435
|
+
let fatal_error_occurred = false;
|
436
|
+
Module.fatal_error = function(e) {
|
437
|
+
if (fatal_error_occurred) {
|
438
|
+
console.error("Recursive call to fatal_error. Inner error was:");
|
439
|
+
console.error(e);
|
440
|
+
return;
|
340
441
|
}
|
341
|
-
|
442
|
+
fatal_error_occurred = true;
|
443
|
+
console.error("Pyodide has suffered a fatal error. " +
|
444
|
+
"Please report this to the Pyodide maintainers.");
|
445
|
+
console.error("The cause of the fatal error was:")
|
446
|
+
console.error(e);
|
447
|
+
try {
|
448
|
+
let fd_stdout = 1;
|
449
|
+
Module.__Py_DumpTraceback(fd_stdout,
|
450
|
+
Module._PyGILState_GetThisThreadState());
|
451
|
+
for (let key of PUBLIC_API) {
|
452
|
+
if (key === "version") {
|
453
|
+
continue;
|
454
|
+
}
|
455
|
+
Object.defineProperty(Module.public_api, key, {
|
456
|
+
enumerable : true,
|
457
|
+
configurable : true,
|
458
|
+
get : () => {
|
459
|
+
throw new Error(
|
460
|
+
"Pyodide already fatally failed and can no longer be used.");
|
461
|
+
}
|
462
|
+
});
|
463
|
+
}
|
464
|
+
if (Module.on_fatal) {
|
465
|
+
Module.on_fatal(e);
|
466
|
+
}
|
467
|
+
} catch (e) {
|
468
|
+
console.error("Another error occurred while handling the fatal error:");
|
469
|
+
console.error(e);
|
470
|
+
}
|
471
|
+
throw e;
|
342
472
|
};
|
343
473
|
|
344
|
-
|
345
|
-
|
346
|
-
|
474
|
+
/**
|
475
|
+
* An alias to the Python :py:mod:`pyodide` package.
|
476
|
+
*
|
477
|
+
* You can use this to call functions defined in the Pyodide Python package
|
478
|
+
* from Javascript.
|
479
|
+
*
|
480
|
+
* @type {PyProxy}
|
481
|
+
*/
|
482
|
+
Module.pyodide_py = {}; // actually defined in runPythonSimple below
|
483
|
+
|
484
|
+
/**
|
485
|
+
*
|
486
|
+
* An alias to the global Python namespace.
|
487
|
+
*
|
488
|
+
* For example, to access a variable called ``foo`` in the Python global
|
489
|
+
* scope, use ``pyodide.globals.get("foo")``
|
490
|
+
*
|
491
|
+
* @type {PyProxy}
|
492
|
+
*/
|
493
|
+
Module.globals = {}; // actually defined in runPythonSimple below
|
494
|
+
|
495
|
+
// clang-format off
|
496
|
+
/**
|
497
|
+
* A Javascript error caused by a Python exception.
|
498
|
+
*
|
499
|
+
* In order to reduce the risk of large memory leaks, the ``PythonError``
|
500
|
+
* contains no reference to the Python exception that caused it. You can find
|
501
|
+
* the actual Python exception that caused this error as `sys.last_value
|
502
|
+
* <https://docs.python.org/3/library/sys.html#sys.last_value>`_.
|
503
|
+
*
|
504
|
+
* See :ref:`type-translations-errors` for more information.
|
505
|
+
*
|
506
|
+
* .. admonition:: Avoid Stack Frames
|
507
|
+
* :class: warning
|
508
|
+
*
|
509
|
+
* If you make a :any:`PyProxy` of ``sys.last_value``, you should be
|
510
|
+
* especially careful to :any:`destroy() <PyProxy.destroy>` it when you are
|
511
|
+
* done. You may leak a large amount of memory including the local
|
512
|
+
* variables of all the stack frames in the traceback if you don't. The
|
513
|
+
* easiest way is to only handle the exception in Python.
|
514
|
+
*
|
515
|
+
* @class
|
516
|
+
*/
|
517
|
+
Module.PythonError = class PythonError {
|
518
|
+
// actually defined in error_handling.c. TODO: would be good to move this
|
519
|
+
// documentation and the definition of PythonError to error_handling.js
|
520
|
+
constructor(){
|
521
|
+
/**
|
522
|
+
* The Python traceback.
|
523
|
+
* @type {string}
|
524
|
+
*/
|
525
|
+
this.message;
|
526
|
+
}
|
347
527
|
};
|
528
|
+
// clang-format on
|
348
529
|
|
349
|
-
|
350
|
-
|
351
|
-
|
352
|
-
|
353
|
-
|
354
|
-
|
355
|
-
|
356
|
-
|
357
|
-
|
358
|
-
|
359
|
-
|
360
|
-
|
361
|
-
|
362
|
-
|
363
|
-
|
364
|
-
|
365
|
-
|
366
|
-
|
367
|
-
|
368
|
-
|
369
|
-
|
370
|
-
|
530
|
+
/**
|
531
|
+
*
|
532
|
+
* The Pyodide version.
|
533
|
+
*
|
534
|
+
* It can be either the exact release version (e.g. ``0.1.0``), or
|
535
|
+
* the latest release version followed by the number of commits since, and
|
536
|
+
* the git hash of the current commit (e.g. ``0.1.0-1-bd84646``).
|
537
|
+
*
|
538
|
+
* @type {string}
|
539
|
+
*/
|
540
|
+
Module.version = ""; // Hack to make jsdoc behave
|
541
|
+
|
542
|
+
/**
|
543
|
+
* Run Python code in the simplest way possible. The primary purpose of this
|
544
|
+
* method is for bootstrapping. It is also useful for debugging: If the Python
|
545
|
+
* interpreter is initialized successfully then it should be possible to use
|
546
|
+
* this method to run Python code even if everything else in the Pyodide
|
547
|
+
* `core` module fails.
|
548
|
+
*
|
549
|
+
* The differences are:
|
550
|
+
* 1. `runPythonSimple` doesn't return anything (and so won't leak
|
551
|
+
* PyProxies)
|
552
|
+
* 2. `runPythonSimple` doesn't require access to any state on the
|
553
|
+
* Javascript `pyodide` module.
|
554
|
+
* 3. `runPython` uses `pyodide.eval_code`, whereas `runPythonSimple` uses
|
555
|
+
* `PyRun_String` which is the C API for `eval` / `exec`.
|
556
|
+
* 4. `runPythonSimple` runs with `globals` a separate dict which is called
|
557
|
+
* `init_dict` (keeps global state private)
|
558
|
+
* 5. `runPythonSimple` doesn't dedent the argument
|
559
|
+
*
|
560
|
+
* When `core` initialization is completed, the globals for `runPythonSimple`
|
561
|
+
* is made available as `Module.init_dict`.
|
562
|
+
*
|
563
|
+
* @private
|
564
|
+
*/
|
565
|
+
Module.runPythonSimple = function(code) {
|
566
|
+
let code_c_string = Module.stringToNewUTF8(code);
|
567
|
+
let errcode;
|
568
|
+
try {
|
569
|
+
errcode = Module._run_python_simple_inner(code_c_string);
|
570
|
+
} catch (e) {
|
571
|
+
Module.fatal_error(e);
|
572
|
+
} finally {
|
573
|
+
Module._free(code_c_string);
|
574
|
+
}
|
575
|
+
if (errcode === -1) {
|
576
|
+
Module._pythonexc2js();
|
577
|
+
}
|
578
|
+
};
|
371
579
|
|
372
|
-
|
373
|
-
|
374
|
-
|
375
|
-
|
376
|
-
|
377
|
-
|
378
|
-
|
379
|
-
|
380
|
-
|
381
|
-
|
382
|
-
|
383
|
-
|
384
|
-
|
385
|
-
|
386
|
-
|
387
|
-
|
388
|
-
// The emscripten module needs to be at this location for the core
|
389
|
-
// filesystem to install itself. Once that's complete, it will be replaced
|
390
|
-
// by the call to `makePublicAPI` with a more limited public API.
|
391
|
-
self.pyodide = pyodide(Module);
|
392
|
-
self.pyodide.loadedPackages = {};
|
393
|
-
self.pyodide.loadPackage = loadPackage;
|
394
|
-
}, () => {});
|
395
|
-
}, () => {});
|
580
|
+
/**
|
581
|
+
* Runs a string of Python code from Javascript.
|
582
|
+
*
|
583
|
+
* The last part of the string may be an expression, in which case, its value
|
584
|
+
* is returned.
|
585
|
+
*
|
586
|
+
* @param {string} code Python code to evaluate
|
587
|
+
* @param {dict} globals An optional Python dictionary to use as the globals.
|
588
|
+
* Defaults to :any:`pyodide.globals`. Uses the Python API
|
589
|
+
* :any:`pyodide.eval_code` to evaluate the code.
|
590
|
+
* @returns The result of the Python code translated to Javascript. See the
|
591
|
+
* documentation for :any:`pyodide.eval_code` for more info.
|
592
|
+
*/
|
593
|
+
Module.runPython = function(code, globals = Module.globals) {
|
594
|
+
return Module.pyodide_py.eval_code(code, globals);
|
595
|
+
};
|
396
596
|
|
397
|
-
|
398
|
-
|
399
|
-
|
400
|
-
|
401
|
-
|
402
|
-
|
403
|
-
|
404
|
-
|
405
|
-
|
406
|
-
|
407
|
-
|
408
|
-
|
409
|
-
|
410
|
-
|
411
|
-
|
412
|
-
|
413
|
-
|
414
|
-
|
415
|
-
|
416
|
-
|
417
|
-
|
418
|
-
|
419
|
-
|
420
|
-
|
421
|
-
|
422
|
-
|
423
|
-
|
424
|
-
|
425
|
-
|
426
|
-
|
427
|
-
|
428
|
-
|
429
|
-
} else {
|
430
|
-
let pre = document.createElement('pre');
|
431
|
-
pre.textContent = val.toString();
|
432
|
-
div.appendChild(pre);
|
433
|
-
element = div;
|
434
|
-
}
|
435
|
-
return element.outerHTML;
|
597
|
+
// clang-format off
|
598
|
+
/**
|
599
|
+
* Inspect a Python code chunk and use :js:func:`pyodide.loadPackage` to
|
600
|
+
* install any known packages that the code chunk imports. Uses the Python API
|
601
|
+
* :func:`pyodide.find\_imports` to inspect the code.
|
602
|
+
*
|
603
|
+
* For example, given the following code as input
|
604
|
+
*
|
605
|
+
* .. code-block:: python
|
606
|
+
*
|
607
|
+
* import numpy as np x = np.array([1, 2, 3])
|
608
|
+
*
|
609
|
+
* :js:func:`loadPackagesFromImports` will call
|
610
|
+
* ``pyodide.loadPackage(['numpy'])``. See also :js:func:`runPythonAsync`.
|
611
|
+
*
|
612
|
+
* @param {string} code The code to inspect.
|
613
|
+
* @param {Function} messageCallback The ``messageCallback`` argument of
|
614
|
+
* :any:`pyodide.loadPackage` (optional).
|
615
|
+
* @param {Function} errorCallback The ``errorCallback`` argument of
|
616
|
+
* :any:`pyodide.loadPackage` (optional).
|
617
|
+
* @async
|
618
|
+
*/
|
619
|
+
Module.loadPackagesFromImports = async function(code, messageCallback, errorCallback) {
|
620
|
+
let imports = Module.pyodide_py.find_imports(code).toJs();
|
621
|
+
if (imports.length === 0) {
|
622
|
+
return;
|
623
|
+
}
|
624
|
+
let packageNames = Module.packages.import_name_to_package_name;
|
625
|
+
let packages = new Set();
|
626
|
+
for (let name of imports) {
|
627
|
+
if (name in packageNames) {
|
628
|
+
packages.add(packageNames[name]);
|
436
629
|
}
|
437
|
-
}
|
438
|
-
|
439
|
-
|
440
|
-
|
630
|
+
}
|
631
|
+
if (packages.size) {
|
632
|
+
await Module.loadPackage(
|
633
|
+
Array.from(packages.keys()), messageCallback, errorCallback
|
634
|
+
);
|
635
|
+
}
|
636
|
+
};
|
637
|
+
// clang-format on
|
638
|
+
|
639
|
+
/**
|
640
|
+
* Access a Python object in the global namespace from Javascript.
|
641
|
+
*
|
642
|
+
* @deprecated This function will be removed in version 0.18.0. Use
|
643
|
+
* :any:`pyodide.globals.get('key') <pyodide.globals>` instead.
|
644
|
+
*
|
645
|
+
* @param {string} name Python variable name
|
646
|
+
* @returns The Python object translated to Javascript.
|
647
|
+
*/
|
648
|
+
Module.pyimport = name => {
|
649
|
+
console.warn(
|
650
|
+
"Access to the Python global namespace via pyodide.pyimport is deprecated and " +
|
651
|
+
"will be removed in version 0.18.0. Use pyodide.globals.get('key') instead.");
|
652
|
+
return Module.globals.get(name);
|
653
|
+
};
|
654
|
+
|
655
|
+
/**
|
656
|
+
* Runs Python code, possibly asynchronously loading any known packages that
|
657
|
+
* the code imports. For example, given the following code
|
658
|
+
*
|
659
|
+
* .. code-block:: python
|
660
|
+
*
|
661
|
+
* import numpy as np
|
662
|
+
* x = np.array([1, 2, 3])
|
663
|
+
*
|
664
|
+
* Pyodide will first call :any:`pyodide.loadPackage(['numpy'])
|
665
|
+
* <pyodide.loadPackage>`, and then run the code using the Python API
|
666
|
+
* :any:`pyodide.eval_code_async`, returning the result. The code is compiled
|
667
|
+
* with `PyCF_ALLOW_TOP_LEVEL_AWAIT
|
668
|
+
* <https://docs.python.org/3/library/ast.html?highlight=pycf_allow_top_level_await#ast.PyCF_ALLOW_TOP_LEVEL_AWAIT>`_.
|
669
|
+
*
|
670
|
+
* For example:
|
671
|
+
*
|
672
|
+
* .. code-block:: pyodide
|
673
|
+
*
|
674
|
+
* let result = await pyodide.runPythonAsync(`
|
675
|
+
* # numpy will automatically be loaded by loadPackagesFromImports
|
676
|
+
* import numpy as np
|
677
|
+
* # we can use top level await
|
678
|
+
* from js import fetch
|
679
|
+
* response = await fetch("./packages.json")
|
680
|
+
* packages = await response.json()
|
681
|
+
* # If final statement is an expression, its value is returned to
|
682
|
+
* Javascript len(packages.dependencies.object_keys())
|
683
|
+
* `);
|
684
|
+
* console.log(result); // 72
|
685
|
+
*
|
686
|
+
* @param {string} code Python code to evaluate
|
687
|
+
* @param {Function} messageCallback The ``messageCallback`` argument of
|
688
|
+
* :any:`pyodide.loadPackage`.
|
689
|
+
* @param {Function} errorCallback The ``errorCallback`` argument of
|
690
|
+
* :any:`pyodide.loadPackage`.
|
691
|
+
* @returns The result of the Python code translated to Javascript.
|
692
|
+
* @async
|
693
|
+
*/
|
694
|
+
Module.runPythonAsync = async function(code, messageCallback, errorCallback) {
|
695
|
+
await Module.loadPackagesFromImports(code, messageCallback, errorCallback);
|
696
|
+
let coroutine = Module.pyodide_py.eval_code_async(code, Module.globals);
|
697
|
+
try {
|
698
|
+
let result = await coroutine;
|
699
|
+
return result;
|
700
|
+
} finally {
|
701
|
+
coroutine.destroy();
|
702
|
+
}
|
703
|
+
};
|
704
|
+
|
705
|
+
// clang-format off
|
706
|
+
/**
|
707
|
+
* Registers the Javascript object ``module`` as a Javascript module named
|
708
|
+
* ``name``. This module can then be imported from Python using the standard
|
709
|
+
* Python import system. If another module by the same name has already been
|
710
|
+
* imported, this won't have much effect unless you also delete the imported
|
711
|
+
* module from ``sys.modules``. This calls the ``pyodide_py`` API
|
712
|
+
* :func:`pyodide.register_js_module`.
|
713
|
+
*
|
714
|
+
* @param {string} name Name of the Javascript module to add
|
715
|
+
* @param {object} module Javascript object backing the module
|
716
|
+
*/
|
717
|
+
Module.registerJsModule = function(name, module) {
|
718
|
+
Module.pyodide_py.register_js_module(name, module);
|
719
|
+
};
|
720
|
+
|
721
|
+
/**
|
722
|
+
* Unregisters a Javascript module with given name that has been previously
|
723
|
+
* registered with :js:func:`pyodide.registerJsModule` or
|
724
|
+
* :func:`pyodide.register_js_module`. If a Javascript module with that name
|
725
|
+
* does not already exist, will throw an error. Note that if the module has
|
726
|
+
* already been imported, this won't have much effect unless you also delete
|
727
|
+
* the imported module from ``sys.modules``. This calls the ``pyodide_py`` API
|
728
|
+
* :func:`pyodide.unregister_js_module`.
|
729
|
+
*
|
730
|
+
* @param {string} name Name of the Javascript module to remove
|
731
|
+
*/
|
732
|
+
Module.unregisterJsModule = function(name) {
|
733
|
+
Module.pyodide_py.unregister_js_module(name);
|
734
|
+
};
|
735
|
+
// clang-format on
|
736
|
+
|
737
|
+
/**
|
738
|
+
* Convert the Javascript object to a Python object as best as possible.
|
739
|
+
*
|
740
|
+
* This is similar to :any:`JsProxy.to_py` but for use from Javascript. If the
|
741
|
+
* object is immutable or a :any:`PyProxy`, it will be returned unchanged. If
|
742
|
+
* the object cannot be converted into Python, it will be returned unchanged.
|
743
|
+
*
|
744
|
+
* See :ref:`type-translations-jsproxy-to-py` for more information.
|
745
|
+
*
|
746
|
+
* @param {*} obj
|
747
|
+
* @param {number} depth Optional argument to limit the depth of the
|
748
|
+
* conversion.
|
749
|
+
* @returns {PyProxy} The object converted to Python.
|
750
|
+
*/
|
751
|
+
Module.toPy = function(obj, depth = -1) {
|
752
|
+
// No point in converting these, it'd be dumb to proxy them so they'd just
|
753
|
+
// get converted back by `js2python` at the end
|
754
|
+
// clang-format off
|
755
|
+
switch (typeof obj) {
|
756
|
+
case "string":
|
757
|
+
case "number":
|
758
|
+
case "boolean":
|
759
|
+
case "bigint":
|
760
|
+
case "undefined":
|
761
|
+
return obj;
|
762
|
+
}
|
763
|
+
// clang-format on
|
764
|
+
if (!obj || Module.isPyProxy(obj)) {
|
765
|
+
return obj;
|
766
|
+
}
|
767
|
+
let obj_id = 0;
|
768
|
+
let py_result = 0;
|
769
|
+
let result = 0;
|
770
|
+
try {
|
771
|
+
obj_id = Module.hiwire.new_value(obj);
|
772
|
+
py_result = Module.__js2python_convert(obj_id, new Map(), depth);
|
773
|
+
// clang-format off
|
774
|
+
if(py_result === 0){
|
775
|
+
// clang-format on
|
776
|
+
Module._pythonexc2js();
|
777
|
+
}
|
778
|
+
if (Module._JsProxy_Check(py_result)) {
|
779
|
+
// Oops, just created a JsProxy. Return the original object.
|
780
|
+
return obj;
|
781
|
+
// return Module.pyproxy_new(py_result);
|
782
|
+
}
|
783
|
+
result = Module._python2js(py_result);
|
784
|
+
// clang-format off
|
785
|
+
if (result === 0) {
|
786
|
+
// clang-format on
|
787
|
+
Module._pythonexc2js();
|
788
|
+
}
|
789
|
+
} finally {
|
790
|
+
Module.hiwire.decref(obj_id);
|
791
|
+
Module._Py_DecRef(py_result);
|
792
|
+
}
|
793
|
+
return Module.hiwire.pop_value(result);
|
794
|
+
};
|
795
|
+
/**
|
796
|
+
* Is the argument a :any:`PyProxy`?
|
797
|
+
* @param jsobj {any} Object to test.
|
798
|
+
* @returns {bool} Is ``jsobj`` a :any:`PyProxy`?
|
799
|
+
*/
|
800
|
+
Module.isPyProxy = function(jsobj) {
|
801
|
+
return !!jsobj && jsobj.$$ !== undefined && jsobj.$$.type === 'PyProxy';
|
802
|
+
};
|
803
|
+
|
804
|
+
Module.locateFile = (path) => baseURL + path;
|
805
|
+
|
806
|
+
let moduleLoaded = new Promise(r => Module.postRun = r);
|
807
|
+
|
808
|
+
const scriptSrc = `${baseURL}pyodide.asm.js`;
|
809
|
+
|
810
|
+
await loadScript(scriptSrc);
|
811
|
+
|
812
|
+
// _createPyodideModule is specified in the Makefile by the linker flag:
|
813
|
+
// `-s EXPORT_NAME="'_createPyodideModule'"`
|
814
|
+
await _createPyodideModule(Module);
|
815
|
+
|
816
|
+
// There is some work to be done between the module being "ready" and postRun
|
817
|
+
// being called.
|
818
|
+
await moduleLoaded;
|
819
|
+
|
820
|
+
// Bootstrap step: `runPython` needs access to `Module.globals` and
|
821
|
+
// `Module.pyodide_py`. Use `runPythonSimple` to add these. runPythonSimple
|
822
|
+
// doesn't dedent the argument so the indentation matters.
|
823
|
+
Module.runPythonSimple(`
|
824
|
+
def temp(Module):
|
825
|
+
import pyodide
|
826
|
+
import __main__
|
827
|
+
import builtins
|
828
|
+
|
829
|
+
globals = __main__.__dict__
|
830
|
+
globals.update(builtins.__dict__)
|
831
|
+
|
832
|
+
Module.version = pyodide.__version__
|
833
|
+
Module.globals = globals
|
834
|
+
Module.builtins = builtins.__dict__
|
835
|
+
Module.pyodide_py = pyodide
|
836
|
+
`);
|
837
|
+
|
838
|
+
Module.saveState = () => Module.pyodide_py._state.save_state();
|
839
|
+
Module.restoreState = (state) =>
|
840
|
+
Module.pyodide_py._state.restore_state(state);
|
841
|
+
|
842
|
+
Module.init_dict.get("temp")(Module);
|
843
|
+
// Module.runPython works starting from here!
|
844
|
+
|
845
|
+
// Wrap "globals" in a special Proxy that allows `pyodide.globals.x` access.
|
846
|
+
// TODO: Should we have this?
|
847
|
+
Module.globals = Module.wrapNamespace(Module.globals);
|
848
|
+
|
849
|
+
let response = await fetch(`${baseURL}packages.json`);
|
850
|
+
Module.packages = await response.json();
|
851
|
+
|
852
|
+
fixRecursionLimit(Module);
|
853
|
+
let pyodide = makePublicAPI(Module, PUBLIC_API);
|
854
|
+
Module.registerJsModule("js", globalThis);
|
855
|
+
Module.registerJsModule("pyodide_js", pyodide);
|
856
|
+
globalThis.pyodide = pyodide;
|
857
|
+
return pyodide;
|
858
|
+
};
|
859
|
+
|
860
|
+
if (globalThis.languagePluginUrl) {
|
861
|
+
console.warn(
|
862
|
+
"languagePluginUrl is deprecated and will be removed in version 0.18.0, " +
|
863
|
+
"instead use loadPyodide({ indexURL : <some_url>})");
|
864
|
+
|
865
|
+
/**
|
866
|
+
* A deprecated parameter that specifies the Pyodide ``indexURL``. If present,
|
867
|
+
* Pyodide will automatically invoke
|
868
|
+
* ``loadPyodide({indexURL : languagePluginUrl})``
|
869
|
+
* and will store the resulting promise in
|
870
|
+
* :any:`globalThis.languagePluginLoader`. Use :any:`loadPyodide`
|
871
|
+
* directly instead of defining this.
|
872
|
+
*
|
873
|
+
* @type String
|
874
|
+
* @deprecated Will be removed in version 0.18.0
|
875
|
+
*/
|
876
|
+
globalThis.languagePluginUrl;
|
877
|
+
|
878
|
+
/**
|
879
|
+
* A deprecated promise that resolves to ``undefined`` when Pyodide is
|
880
|
+
* finished loading. Only created if :any:`languagePluginUrl` is
|
881
|
+
* defined. Instead use :any:`loadPyodide`.
|
882
|
+
*
|
883
|
+
* @type Promise
|
884
|
+
* @deprecated Will be removed in version 0.18.0
|
885
|
+
*/
|
886
|
+
globalThis.languagePluginLoader =
|
887
|
+
loadPyodide({indexURL : globalThis.languagePluginUrl});
|
888
|
+
}
|