pyodide 0.29.2 → 314.0.0-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/console-v2.html CHANGED
@@ -188,13 +188,29 @@ of the stable console.
188
188
  const history = [];
189
189
  let historyIndex = null; // null means not navigating history
190
190
 
191
+ // Load history from localStorage
192
+ try {
193
+ const savedHistory = localStorage.getItem("0_commands");
194
+ if (savedHistory) {
195
+ const parsed = JSON.parse(savedHistory);
196
+ if (Array.isArray(parsed)) {
197
+ history.push(...parsed);
198
+ }
199
+ }
200
+ } catch (e) {
201
+ console.error("Failed to load history from localStorage:", e);
202
+ }
203
+
191
204
  term.write(prompt);
192
205
 
193
206
  function addToHistory(command) {
194
207
  const trimmed = command.trimEnd();
195
208
  if (!trimmed) return;
196
209
  const last = history[history.length - 1];
197
- if (last !== trimmed) history.push(trimmed);
210
+ if (last !== trimmed) {
211
+ history.push(trimmed);
212
+ localStorage.setItem("0_commands", JSON.stringify(history));
213
+ }
198
214
  }
199
215
 
200
216
  function refreshLine() {
@@ -217,6 +233,112 @@ of the stable console.
217
233
  refreshLine();
218
234
  }
219
235
 
236
+ function insertAtCursor(text) {
237
+ const before = buffer.slice(0, cursorIndex);
238
+ const after = buffer.slice(cursorIndex);
239
+ setBuffer(before + text + after, cursorIndex + text.length);
240
+ }
241
+
242
+ function clearBuffer() {
243
+ buffer = "";
244
+ cursorIndex = 0;
245
+ }
246
+
247
+ function longestCommonPrefix(strings) {
248
+ if (strings.length === 0) return "";
249
+ return strings.reduce((a, b) => {
250
+ let i = 0;
251
+ while (i < a.length && i < b.length && a[i] === b[i]) i++;
252
+ return a.slice(0, i);
253
+ });
254
+ }
255
+
256
+ async function handleTabCompletion() {
257
+ const sourceToComplete = buffer.slice(0, cursorIndex);
258
+ if (!sourceToComplete.trim()) {
259
+ return;
260
+ }
261
+ if (/[([{]\s*$/.test(sourceToComplete)) {
262
+ return;
263
+ }
264
+
265
+ const completionResult = pyconsole.complete(sourceToComplete);
266
+ const completions = completionResult[0].toJs();
267
+ const start = completionResult[1];
268
+ completionResult.destroy();
269
+
270
+ const currentWord = sourceToComplete.slice(start);
271
+ if (!currentWord.trim() || /^\d+$/.test(currentWord.trim())) {
272
+ return;
273
+ }
274
+
275
+ if (completions.length === 1) {
276
+ const newBuf =
277
+ buffer.slice(0, start) +
278
+ completions[0] +
279
+ buffer.slice(cursorIndex);
280
+ setBuffer(newBuf, start + completions[0].length);
281
+ } else if (completions.length > 1) {
282
+ const prefix = longestCommonPrefix(completions);
283
+ if (prefix.length > currentWord.length) {
284
+ const newBuf =
285
+ buffer.slice(0, start) +
286
+ prefix +
287
+ buffer.slice(cursorIndex);
288
+ setBuffer(newBuf, start + prefix.length);
289
+ } else {
290
+ term.write("\r\n");
291
+ const displayCompletions = completions.slice(0, 200);
292
+ term.writeln(displayCompletions.join(" "));
293
+ if (completions.length > 200) {
294
+ term.writeln(`... and ${completions.length - 200} more`);
295
+ }
296
+ refreshLine();
297
+ }
298
+ }
299
+ }
300
+
301
+ async function handlePaste(data) {
302
+ const normalized = data.replace(/\r\n?/g, "\n");
303
+ if (normalized.includes("\n")) {
304
+ const hasTrailingNewline = normalized.endsWith("\n");
305
+ const lines = normalized.split("\n");
306
+ if (hasTrailingNewline) {
307
+ lines.pop();
308
+ }
309
+
310
+ const cleanedLines = lines.map((line) =>
311
+ line.replace(/^\s*(>>>|\.\.\.)\s?/, "")
312
+ );
313
+
314
+ const executableLines = hasTrailingNewline
315
+ ? cleanedLines
316
+ : cleanedLines.slice(0, -1);
317
+
318
+ for (const line of executableLines) {
319
+ insertAtCursor(line);
320
+ term.write("\r\n");
321
+ await execLine(buffer);
322
+ clearBuffer();
323
+ }
324
+
325
+ if (!hasTrailingNewline) {
326
+ const trailingFragment =
327
+ cleanedLines.length > 0
328
+ ? cleanedLines[cleanedLines.length - 1]
329
+ : "";
330
+ if (trailingFragment) {
331
+ insertAtCursor(trailingFragment);
332
+ }
333
+ } else {
334
+ term.write(prompt);
335
+ }
336
+ } else {
337
+ // Single line paste
338
+ insertAtCursor(data);
339
+ }
340
+ }
341
+
220
342
  async function execLine(line) {
221
343
  // Normalize non-breaking spaces to regular spaces
222
344
  line = line.replace(/\u00a0/g, " ");
@@ -278,14 +400,12 @@ of the stable console.
278
400
  case "\r": // Enter
279
401
  term.write("\r\n");
280
402
  await execLine(buffer);
281
- buffer = "";
282
- cursorIndex = 0;
403
+ clearBuffer();
283
404
  term.write(prompt);
284
405
  break;
285
406
  case "\u0003": // Ctrl-C
286
407
  pyconsole.buffer.clear();
287
- buffer = "";
288
- cursorIndex = 0;
408
+ clearBuffer();
289
409
  term.write("^C\r\nKeyboardInterrupt\r\n" + ps1);
290
410
  prompt = ps1;
291
411
  historyIndex = null;
@@ -293,11 +413,7 @@ of the stable console.
293
413
  case "\u0016": // Ctrl-V
294
414
  // paste from clipboard
295
415
  const clipboard = await navigator.clipboard.readText();
296
- const newBuf =
297
- buffer.slice(0, cursorIndex) +
298
- clipboard +
299
- buffer.slice(cursorIndex);
300
- setBuffer(newBuf, newBuf.length);
416
+ await handlePaste(clipboard);
301
417
  break;
302
418
  case "\u007F": // Backspace
303
419
  if (cursorIndex > 0) {
@@ -341,16 +457,17 @@ of the stable console.
341
457
  refreshLine();
342
458
  }
343
459
  break;
460
+ case "\x1B[Z": // Shift+Tab - ignore to prevent clearing input
461
+ break;
462
+ case "\t":
463
+ await handleTabCompletion();
464
+ break;
344
465
  default:
345
466
  if (data) {
346
467
  // Normalize non-breaking spaces to regular spaces
347
468
  data = data.replace(/\u00a0/g, " ");
348
- // Insert arbitrary string at cursor position
349
- const before = buffer.slice(0, cursorIndex);
350
- const after = buffer.slice(cursorIndex);
351
- const newBuf = before + data + after;
352
- const newCursor = cursorIndex + data.length;
353
- setBuffer(newBuf, newCursor);
469
+ // Handle multiline paste
470
+ await handlePaste(data);
354
471
  }
355
472
  }
356
473
  });
package/ffi.d.ts CHANGED
@@ -7,11 +7,17 @@ export type TypedArray = Int8Array | Uint8Array | Int16Array | Uint16Array | Int
7
7
  interface PyProxy {
8
8
  [x: string]: any;
9
9
  }
10
+ declare const dispose: symbol;
10
11
  /**
11
12
  * A :js:class:`~pyodide.ffi.PyProxy` is an object that allows idiomatic use of a Python object from
12
13
  * JavaScript. See :ref:`type-translations-pyproxy`.
13
14
  */
14
15
  declare class PyProxy {
16
+ /**
17
+ * JavaScript resource management
18
+ * https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Resource_management#the_using_and_await_using_declarations
19
+ */
20
+ [Symbol.dispose]: () => void;
15
21
  /** @private */
16
22
  $$flags: number;
17
23
  /** @private */
@@ -126,7 +132,7 @@ declare class PyProxy {
126
132
  */
127
133
  declare class PyProxyWithLength extends PyProxy {
128
134
  /** @private */
129
- static [Symbol.hasInstance](obj: any): obj is PyProxy;
135
+ static [Symbol.hasInstance](obj: any): obj is PyProxyWithLength;
130
136
  }
131
137
  interface PyProxyWithLength extends PyLengthMethods {
132
138
  }
@@ -142,7 +148,7 @@ declare class PyLengthMethods {
142
148
  */
143
149
  declare class PyProxyWithGet extends PyProxy {
144
150
  /** @private */
145
- static [Symbol.hasInstance](obj: any): obj is PyProxy;
151
+ static [Symbol.hasInstance](obj: any): obj is PyProxyWithGet;
146
152
  }
147
153
  interface PyProxyWithGet extends PyGetItemMethods {
148
154
  }
@@ -176,7 +182,7 @@ declare class PyGetItemMethods {
176
182
  */
177
183
  declare class PyProxyWithSet extends PyProxy {
178
184
  /** @private */
179
- static [Symbol.hasInstance](obj: any): obj is PyProxy;
185
+ static [Symbol.hasInstance](obj: any): obj is PyProxyWithSet;
180
186
  }
181
187
  interface PyProxyWithSet extends PySetItemMethods {
182
188
  }
@@ -201,7 +207,7 @@ declare class PySetItemMethods {
201
207
  */
202
208
  declare class PyProxyWithHas extends PyProxy {
203
209
  /** @private */
204
- static [Symbol.hasInstance](obj: any): obj is PyProxy;
210
+ static [Symbol.hasInstance](obj: any): obj is PyProxyWithHas;
205
211
  }
206
212
  interface PyProxyWithHas extends PyContainsMethods {
207
213
  }
@@ -220,7 +226,7 @@ declare class PyContainsMethods {
220
226
  */
221
227
  declare class PyIterable extends PyProxy {
222
228
  /** @private */
223
- static [Symbol.hasInstance](obj: any): obj is PyProxy;
229
+ static [Symbol.hasInstance](obj: any): obj is PyIterable;
224
230
  }
225
231
  interface PyIterable extends PyIterableMethods {
226
232
  }
@@ -240,7 +246,7 @@ declare class PyIterableMethods {
240
246
  */
241
247
  declare class PyAsyncIterable extends PyProxy {
242
248
  /** @private */
243
- static [Symbol.hasInstance](obj: any): obj is PyProxy;
249
+ static [Symbol.hasInstance](obj: any): obj is PyAsyncIterable;
244
250
  }
245
251
  interface PyAsyncIterable extends PyAsyncIterableMethods {
246
252
  }
@@ -259,7 +265,7 @@ declare class PyAsyncIterableMethods {
259
265
  */
260
266
  declare class PyIterator extends PyProxy {
261
267
  /** @private */
262
- static [Symbol.hasInstance](obj: any): obj is PyProxy;
268
+ static [Symbol.hasInstance](obj: any): obj is PyIterator;
263
269
  }
264
270
  interface PyIterator extends PyIteratorMethods {
265
271
  }
@@ -288,7 +294,7 @@ declare class PyIteratorMethods {
288
294
  */
289
295
  declare class PyGenerator extends PyProxy {
290
296
  /** @private */
291
- static [Symbol.hasInstance](obj: any): obj is PyProxy;
297
+ static [Symbol.hasInstance](obj: any): obj is PyGenerator;
292
298
  }
293
299
  interface PyGenerator extends PyGeneratorMethods {
294
300
  }
@@ -331,7 +337,7 @@ declare class PyGeneratorMethods {
331
337
  */
332
338
  declare class PyAsyncIterator extends PyProxy {
333
339
  /** @private */
334
- static [Symbol.hasInstance](obj: any): obj is PyProxy;
340
+ static [Symbol.hasInstance](obj: any): obj is PyAsyncIterator;
335
341
  }
336
342
  interface PyAsyncIterator extends PyAsyncIteratorMethods {
337
343
  }
@@ -361,7 +367,7 @@ declare class PyAsyncIteratorMethods {
361
367
  */
362
368
  declare class PyAsyncGenerator extends PyProxy {
363
369
  /** @private */
364
- static [Symbol.hasInstance](obj: any): obj is PyProxy;
370
+ static [Symbol.hasInstance](obj: any): obj is PyAsyncGenerator;
365
371
  }
366
372
  interface PyAsyncGenerator extends PyAsyncGeneratorMethods {
367
373
  }
@@ -403,7 +409,7 @@ declare class PyAsyncGeneratorMethods {
403
409
  */
404
410
  declare class PySequence extends PyProxy {
405
411
  /** @private */
406
- static [Symbol.hasInstance](obj: any): obj is PyProxy;
412
+ static [Symbol.hasInstance](obj: any): obj is PySequence;
407
413
  }
408
414
  interface PySequence extends PySequenceMethods {
409
415
  }
@@ -603,7 +609,7 @@ declare class PySequenceMethods {
603
609
  */
604
610
  declare class PyMutableSequence extends PyProxy {
605
611
  /** @private */
606
- static [Symbol.hasInstance](obj: any): obj is PyProxy;
612
+ static [Symbol.hasInstance](obj: any): obj is PyMutableSequence;
607
613
  }
608
614
  interface PyMutableSequence extends PyMutableSequenceMethods {
609
615
  }
@@ -690,7 +696,7 @@ declare class PyMutableSequenceMethods {
690
696
  */
691
697
  declare class PyAwaitable extends PyProxy {
692
698
  /** @private */
693
- static [Symbol.hasInstance](obj: any): obj is PyProxy;
699
+ static [Symbol.hasInstance](obj: any): obj is PyAwaitable;
694
700
  }
695
701
  interface PyAwaitable extends Promise<any> {
696
702
  }
@@ -892,6 +898,11 @@ declare class PyBufferMethods {
892
898
  * data, so you might want to pass ``'dataview'`` as the type argument in that
893
899
  * case.
894
900
  *
901
+ * When you are done with the buffer view, you have to call
902
+ * :js:func:`~PyBufferView.release`. Alternatively, if you declare the buffer
903
+ * with `using pybuf = proxy.getBuffer()`, JavaScript will automatically
904
+ * release the buffer at the end of the current scope.
905
+ *
895
906
  * @param type The type of the :js:attr:`~pyodide.ffi.PyBufferView.data` field
896
907
  * in the output. Should be one of: ``"i8"``, ``"u8"``, ``"u8clamped"``,
897
908
  * ``"i16"``, ``"u16"``, ``"i32"``, ``"u32"``, ``"i32"``, ``"u32"``,
@@ -907,16 +918,19 @@ declare class PyBufferMethods {
907
918
  */
908
919
  declare class PyDict extends PyProxy {
909
920
  /** @private */
910
- static [Symbol.hasInstance](obj: any): obj is PyProxy;
921
+ static [Symbol.hasInstance](obj: any): obj is PyDict;
911
922
  }
912
923
  interface PyDict extends PyProxyWithGet, PyProxyWithSet, PyProxyWithHas, PyProxyWithLength, PyIterable {
913
924
  }
914
925
  /**
915
926
  * A class to allow access to Python data buffers from JavaScript. These are
916
- * produced by :js:meth:`~pyodide.ffi.PyBuffer.getBuffer` and cannot be constructed directly.
917
- * When you are done, release it with the :js:func:`~PyBufferView.release` method.
918
- * See the Python :external:doc:`c-api/buffer` documentation for more
919
- * information.
927
+ * produced by :js:meth:`~pyodide.ffi.PyBuffer.getBuffer` and cannot be
928
+ * constructed directly. When you are done, release it with the
929
+ * :js:func:`~PyBufferView.release` method. It has a `[Symbol.dispose]()` method
930
+ * which is identical to the `release` method, so if you create the buffer with
931
+ * `using pybuf = proxy.getBuffer();` and JavaScript will automatically release
932
+ * it at the end of the scope. See the Python :external:doc:`c-api/buffer`
933
+ * documentation for more information.
920
934
  *
921
935
  * To find the element ``x[a_1, ..., a_n]``, you could use the following code:
922
936
  *
@@ -964,6 +978,7 @@ interface PyDict extends PyProxyWithGet, PyProxyWithSet, PyProxyWithHas, PyProxy
964
978
  * );
965
979
  */
966
980
  declare class PyBufferView {
981
+ [Symbol.dispose]: () => void;
967
982
  /**
968
983
  * The offset of the first entry of the array. For instance if our array
969
984
  * is 3d, then you will find ``array[0,0,0]`` at
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pyodide",
3
- "version": "0.29.2",
3
+ "version": "314.0.0-alpha.1",
4
4
  "description": "The Pyodide JavaScript package",
5
5
  "keywords": [
6
6
  "python",
@@ -30,15 +30,14 @@
30
30
  "dts-bundle-generator": "^8.1.1",
31
31
  "esbuild": "^0.25.0",
32
32
  "express": "^4.17.3",
33
- "mocha": "^9.0.2",
34
33
  "npm-run-all": "^4.1.5",
35
34
  "nyc": "^15.1.0",
36
35
  "playwright": "^1.55.1",
37
36
  "prettier": "^2.2.1",
38
37
  "tsd": "^0.24.1",
39
38
  "tsx": "^4.20.5",
40
- "typedoc": "^0.27.6",
41
- "typescript": "5.7",
39
+ "typedoc": "^0.27.9",
40
+ "typescript": "5.8",
42
41
  "wabt": "^1.0.32"
43
42
  },
44
43
  "main": "pyodide.js",
@@ -52,7 +51,7 @@
52
51
  "types": "./ffi.d.ts"
53
52
  },
54
53
  "./pyodide.asm.wasm": "./pyodide.asm.wasm",
55
- "./pyodide.asm.js": "./pyodide.asm.js",
54
+ "./pyodide.asm.mjs": "./pyodide.asm.mjs",
56
55
  "./python_stdlib.zip": "./python_stdlib.zip",
57
56
  "./pyodide.mjs": "./pyodide.mjs",
58
57
  "./pyodide.js": "./pyodide.js",
@@ -60,7 +59,7 @@
60
59
  "./pyodide-lock.json": "./pyodide-lock.json"
61
60
  },
62
61
  "files": [
63
- "pyodide.asm.js",
62
+ "pyodide.asm.mjs",
64
63
  "pyodide.asm.wasm",
65
64
  "python_stdlib.zip",
66
65
  "pyodide.mjs",