gwchq-textjam 0.1.110 → 0.1.112
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/dist/assets/{PyodideWorker54bb8b9ad9a6ac4abf31.js → PyodideWorkera79b61f3c8f551703270.js}
RENAMED
|
@@ -7,11 +7,68 @@ function toAbsoluteFromOrigin(url) {
|
|
|
7
7
|
return new URL(url, self.location.origin).href;
|
|
8
8
|
}
|
|
9
9
|
|
|
10
|
-
// removing submodules (matplotlib.pyplot = matplotlib)
|
|
11
|
-
const normalizeImportName = (name) => name.split(".")[0];
|
|
12
|
-
|
|
13
10
|
const WORKING_DIR = "/home/pyodide";
|
|
14
11
|
|
|
12
|
+
const toStructuredCloneable = (value) => {
|
|
13
|
+
if (value == null) return value;
|
|
14
|
+
|
|
15
|
+
if (
|
|
16
|
+
typeof value === "string" ||
|
|
17
|
+
typeof value === "number" ||
|
|
18
|
+
typeof value === "boolean"
|
|
19
|
+
) {
|
|
20
|
+
return value;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
if (value instanceof Uint8Array) {
|
|
24
|
+
return value.slice();
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
if (ArrayBuffer.isView(value)) {
|
|
28
|
+
return new Uint8Array(
|
|
29
|
+
value.buffer.slice(value.byteOffset, value.byteOffset + value.byteLength),
|
|
30
|
+
);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
if (value instanceof ArrayBuffer) {
|
|
34
|
+
return value.slice(0);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
if (value instanceof Map) {
|
|
38
|
+
return Object.fromEntries(
|
|
39
|
+
[...value.entries()].map(([k, v]) => [k, toStructuredCloneable(v)]),
|
|
40
|
+
);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
if (Array.isArray(value)) {
|
|
44
|
+
return value.map(toStructuredCloneable);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
if (typeof value?.toJs === "function") {
|
|
48
|
+
try {
|
|
49
|
+
const converted = value.toJs({ dict_converter: Object.fromEntries });
|
|
50
|
+
return toStructuredCloneable(converted);
|
|
51
|
+
} catch (_) {
|
|
52
|
+
try {
|
|
53
|
+
const converted = value.toJs();
|
|
54
|
+
return toStructuredCloneable(converted);
|
|
55
|
+
} catch (_) {
|
|
56
|
+
return String(value);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
if (typeof value === "object") {
|
|
62
|
+
const result = {};
|
|
63
|
+
for (const [key, val] of Object.entries(value)) {
|
|
64
|
+
result[key] = toStructuredCloneable(val);
|
|
65
|
+
}
|
|
66
|
+
return result;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
return String(value);
|
|
70
|
+
};
|
|
71
|
+
|
|
15
72
|
// Nest the PyodideWorker function inside a globalThis object so we control when its initialised.
|
|
16
73
|
const PyodideWorker = () => {
|
|
17
74
|
let assets;
|
|
@@ -66,9 +123,8 @@ const PyodideWorker = () => {
|
|
|
66
123
|
let suppressInternalStdStreams = false;
|
|
67
124
|
|
|
68
125
|
const onmessage = async ({ data }) => {
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
}
|
|
126
|
+
pyodide = await pyodidePromise;
|
|
127
|
+
let encoder = new TextEncoder();
|
|
72
128
|
|
|
73
129
|
switch (data.method) {
|
|
74
130
|
case "stdinResponse":
|
|
@@ -96,7 +152,6 @@ const PyodideWorker = () => {
|
|
|
96
152
|
}
|
|
97
153
|
break;
|
|
98
154
|
case "writeFile":
|
|
99
|
-
const encoder = new TextEncoder();
|
|
100
155
|
pyodide.FS.writeFile(
|
|
101
156
|
`${WORKING_DIR}/${data.filename}`,
|
|
102
157
|
encoder.encode(data.content),
|
|
@@ -188,12 +243,27 @@ const PyodideWorker = () => {
|
|
|
188
243
|
|
|
189
244
|
MAX_FILES = 100
|
|
190
245
|
MAX_FILE_SIZE = 8500000
|
|
246
|
+
PROJECT_ROOT = os.path.abspath("${WORKING_DIR}")
|
|
247
|
+
|
|
248
|
+
def _is_project_file(filename):
|
|
249
|
+
abs_path = os.path.abspath(filename)
|
|
250
|
+
return abs_path == PROJECT_ROOT or abs_path.startswith(PROJECT_ROOT + os.sep)
|
|
251
|
+
|
|
252
|
+
def _to_project_relative(filename):
|
|
253
|
+
abs_path = os.path.abspath(filename)
|
|
254
|
+
return os.path.relpath(abs_path, PROJECT_ROOT)
|
|
191
255
|
|
|
192
256
|
def _custom_open(filename, mode="r", *args, **kwargs):
|
|
193
|
-
|
|
257
|
+
abs_path = os.path.abspath(filename)
|
|
258
|
+
|
|
259
|
+
if "x" in mode and os.path.exists(abs_path):
|
|
194
260
|
raise FileExistsError(f"File '{filename}' already exists")
|
|
195
|
-
|
|
196
|
-
|
|
261
|
+
|
|
262
|
+
is_text_write = ("w" in mode or "a" in mode or "x" in mode) and "b" not in mode
|
|
263
|
+
is_project_file = _is_project_file(abs_path)
|
|
264
|
+
|
|
265
|
+
if is_text_write and is_project_file:
|
|
266
|
+
if len(os.listdir(PROJECT_ROOT)) > MAX_FILES and not os.path.exists(abs_path):
|
|
197
267
|
raise OSError(f"File system limit reached, no more than {MAX_FILES} files allowed")
|
|
198
268
|
class CustomFile:
|
|
199
269
|
def __init__(self, filename):
|
|
@@ -204,9 +274,13 @@ const PyodideWorker = () => {
|
|
|
204
274
|
self.content += content
|
|
205
275
|
if len(self.content) > MAX_FILE_SIZE:
|
|
206
276
|
raise OSError(f"File '{self.filename}' exceeds maximum file size of {MAX_FILE_SIZE} bytes")
|
|
207
|
-
with _original_open(self.filename, mode) as f:
|
|
277
|
+
with _original_open(self.filename, mode, *args, **kwargs) as f:
|
|
208
278
|
f.write(self.content)
|
|
209
|
-
basthon.kernel.write_file({
|
|
279
|
+
basthon.kernel.write_file({
|
|
280
|
+
"filename": _to_project_relative(self.filename),
|
|
281
|
+
"content": self.content,
|
|
282
|
+
"mode": mode
|
|
283
|
+
})
|
|
210
284
|
|
|
211
285
|
def close(self):
|
|
212
286
|
pass
|
|
@@ -217,7 +291,7 @@ const PyodideWorker = () => {
|
|
|
217
291
|
def __exit__(self, exc_type, exc_val, exc_tb):
|
|
218
292
|
self.close()
|
|
219
293
|
|
|
220
|
-
return CustomFile(
|
|
294
|
+
return CustomFile(abs_path)
|
|
221
295
|
else:
|
|
222
296
|
return _original_open(filename, mode, *args, **kwargs)
|
|
223
297
|
|
|
@@ -234,18 +308,16 @@ const PyodideWorker = () => {
|
|
|
234
308
|
|
|
235
309
|
for (let name of imports) {
|
|
236
310
|
checkIfStopped();
|
|
237
|
-
|
|
238
|
-
await vendoredPackages[pkgName]?.after();
|
|
311
|
+
await vendoredPackages[name]?.after();
|
|
239
312
|
}
|
|
240
313
|
};
|
|
241
314
|
|
|
242
315
|
const loadDependency = async (name) => {
|
|
243
316
|
checkIfStopped();
|
|
244
317
|
|
|
245
|
-
const pkgName = normalizeImportName(name);
|
|
246
318
|
// If the import is for another user file then open it and load its dependencies.
|
|
247
|
-
if (pyodide.FS.readdir(WORKING_DIR).includes(`${
|
|
248
|
-
const fileContent = pyodide.FS.readFile(`${WORKING_DIR}/${
|
|
319
|
+
if (pyodide.FS.readdir(WORKING_DIR).includes(`${name}.py`)) {
|
|
320
|
+
const fileContent = pyodide.FS.readFile(`${WORKING_DIR}/${name}.py`, {
|
|
249
321
|
encoding: "utf8",
|
|
250
322
|
});
|
|
251
323
|
await withSupportForPackages(fileContent);
|
|
@@ -253,30 +325,31 @@ const PyodideWorker = () => {
|
|
|
253
325
|
}
|
|
254
326
|
|
|
255
327
|
// If the import is for a vendored package then run its .before() hook.
|
|
256
|
-
const vendoredPackage = vendoredPackages[
|
|
328
|
+
const vendoredPackage = vendoredPackages[name];
|
|
257
329
|
await vendoredPackage?.before();
|
|
258
330
|
if (vendoredPackage) {
|
|
259
331
|
return;
|
|
260
332
|
}
|
|
261
333
|
|
|
262
334
|
// If the import is for a module built into Python then do nothing.
|
|
335
|
+
let pythonModule;
|
|
263
336
|
try {
|
|
264
|
-
|
|
265
|
-
if (pythonModule) {
|
|
266
|
-
return;
|
|
267
|
-
}
|
|
337
|
+
pythonModule = pyodide.pyimport(name);
|
|
268
338
|
} catch (_) {}
|
|
339
|
+
if (pythonModule) {
|
|
340
|
+
return;
|
|
341
|
+
}
|
|
269
342
|
|
|
270
343
|
// If the import is for a package built into Pyodide then load it.
|
|
271
344
|
// Built-ins: https://pyodide.org/en/stable/usage/packages-in-pyodide.html
|
|
345
|
+
await pyodide.loadPackage(name)?.catch(() => {});
|
|
346
|
+
let pyodidePackage;
|
|
272
347
|
try {
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
const pyodidePackage = pyodide.pyimport(pkgName);
|
|
276
|
-
if (pyodidePackage) {
|
|
277
|
-
return;
|
|
278
|
-
}
|
|
348
|
+
pyodidePackage = pyodide.pyimport(name);
|
|
279
349
|
} catch (_) {}
|
|
350
|
+
if (pyodidePackage) {
|
|
351
|
+
return;
|
|
352
|
+
}
|
|
280
353
|
|
|
281
354
|
// Ensure micropip is loaded which can fetch packages from PyPi.
|
|
282
355
|
// See: https://pyodide.org/en/stable/usage/loading-packages.html
|
|
@@ -287,7 +360,7 @@ const PyodideWorker = () => {
|
|
|
287
360
|
|
|
288
361
|
// If the import is for a PyPi package then load it.
|
|
289
362
|
// Otherwise, don't error now so that we get an error later from Python.
|
|
290
|
-
await pyodide.micropip.install(
|
|
363
|
+
await pyodide.micropip.install(name).catch(() => {});
|
|
291
364
|
};
|
|
292
365
|
|
|
293
366
|
const vendoredPackages = {
|
|
@@ -457,15 +530,33 @@ const PyodideWorker = () => {
|
|
|
457
530
|
const fakeBasthonPackage = {
|
|
458
531
|
kernel: {
|
|
459
532
|
display_event: (event) => {
|
|
460
|
-
|
|
461
|
-
|
|
533
|
+
let payload;
|
|
534
|
+
try {
|
|
535
|
+
payload = event.toJs({ dict_converter: Object.fromEntries });
|
|
536
|
+
} catch (_) {
|
|
537
|
+
payload = event.toJs();
|
|
538
|
+
}
|
|
539
|
+
|
|
540
|
+
const origin = String(payload.display_type);
|
|
541
|
+
const content = toStructuredCloneable(payload.content);
|
|
462
542
|
|
|
463
543
|
postMessage({ method: "handleVisual", origin, content });
|
|
464
544
|
},
|
|
465
545
|
write_file: (event) => {
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
546
|
+
let payload;
|
|
547
|
+
try {
|
|
548
|
+
payload = event.toJs({ dict_converter: Object.fromEntries });
|
|
549
|
+
} catch (_) {
|
|
550
|
+
payload = event.toJs();
|
|
551
|
+
}
|
|
552
|
+
|
|
553
|
+
const filename = String(payload.filename);
|
|
554
|
+
const content =
|
|
555
|
+
typeof payload.content === "string"
|
|
556
|
+
? payload.content
|
|
557
|
+
: toStructuredCloneable(payload.content);
|
|
558
|
+
const mode = String(payload.mode);
|
|
559
|
+
|
|
469
560
|
postMessage({ method: "handleFileWrite", filename, content, mode });
|
|
470
561
|
},
|
|
471
562
|
locals: () => pyodide.runPython("globals()"),
|
|
@@ -513,7 +604,6 @@ const PyodideWorker = () => {
|
|
|
513
604
|
|
|
514
605
|
pyodidePromise = loadPyodide({
|
|
515
606
|
stdout: (content) => {
|
|
516
|
-
if (stopped) return;
|
|
517
607
|
if (userStdStreamsEnabled && !suppressInternalStdStreams) {
|
|
518
608
|
postMessage({ method: "handleOutput", stream: "stdout", content });
|
|
519
609
|
} else {
|
|
@@ -521,7 +611,6 @@ const PyodideWorker = () => {
|
|
|
521
611
|
}
|
|
522
612
|
},
|
|
523
613
|
stderr: (content) => {
|
|
524
|
-
if (stopped) return;
|
|
525
614
|
if (userStdStreamsEnabled && !suppressInternalStdStreams) {
|
|
526
615
|
postMessage({ method: "handleOutput", stream: "stderr", content });
|
|
527
616
|
} else {
|
|
@@ -646,7 +735,13 @@ const PyodideWorker = () => {
|
|
|
646
735
|
|
|
647
736
|
return { file, line, mistake, type, info };
|
|
648
737
|
};
|
|
738
|
+
|
|
739
|
+
// return {
|
|
740
|
+
// postMessage,
|
|
741
|
+
// onmessage,
|
|
742
|
+
// };
|
|
649
743
|
};
|
|
650
744
|
|
|
651
745
|
globalThis.PyodideWorker = PyodideWorker;
|
|
652
746
|
PyodideWorker();
|
|
747
|
+
// export default PyodideWorker;
|