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.
@@ -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
- if (data.method !== "init") {
70
- pyodide = await pyodidePromise;
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
- if "x" in mode and os.path.exists(filename):
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
- if ("w" in mode or "a" in mode or "x" in mode) and "b" not in mode:
196
- if len(os.listdir()) > MAX_FILES and not os.path.exists(filename):
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({ "filename": self.filename, "content": self.content, "mode": mode })
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(filename)
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
- const pkgName = normalizeImportName(name);
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(`${pkgName}.py`)) {
248
- const fileContent = pyodide.FS.readFile(`${WORKING_DIR}/${pkgName}.py`, {
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[pkgName];
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
- const pythonModule = pyodide.pyimport(pkgName);
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
- await pyodide.loadPackage(pkgName);
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(pkgName).catch(() => {});
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
- const origin = event.toJs().get("display_type");
461
- const content = event.toJs().get("content");
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
- const filename = event.toJs().get("filename");
467
- const content = event.toJs().get("content");
468
- const mode = event.toJs().get("mode");
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;