concept-collection / jupyterlite-numbl-kernel
Support input() (stdin)
A cell that calls input() now prompts in the notebook and blocks until the user answers, then resumes with their entry — numeric, or a string with input(prompt, 's'). It uses numbl 0.4.16's browser stdin channel over a SharedArrayBuffer, so it needs the page cross-origin isolated (same as interrupt); on a non-isolated deployment input() raises instead of prompting. - kernel: onInputRequest -> this.inputRequest (Jupyter stdin input_request); inputReply -> session.provideInput(). The input_reply reaches the busy kernel because JupyterLite delivers it out of band, bypassing the message mutex the running cell holds. - bump numbl to ^0.4.16 — its browser stdin API plus the JIT bare-literal `ans` fix, without which numeric input()/eval() of a plain number evaluated to 0. - document input() in the README.
Jeremy Magland <jmagland@flatironinstitute.org> committed commit 851d14e84767 parent cf2d698 Browse files
4 changed files+37−20
README.mdmodified+14−11View file
@@ -140,11 +140,12 @@ degrades to a no-op there.
140140 restarting the kernel), and a **tight loop with no function or builtin
141141 calls** (e.g. `while true; x = x + 1; end`) is JIT-compiled straight
142142 through with no cancellation checkpoint, so it too needs a restart.
143-- **No `input()`** (stdin): numbl's browser session has no stdin channel in
144- its worker protocol yet, so `input()` is unsupported. (This is now a
145- missing feature, not a headers limitation — the demo is cross-origin
146- isolated for interrupt, so the `SharedArrayBuffer` such a channel would
147- need is available.)
143+- **`input()`** (stdin) works: a cell that calls `input()` prompts in the
144+ notebook and blocks until you answer, then resumes with your entry —
145+ numeric (`n = input('n? ')`) or, with `input(prompt, 's')`, a string. Like
146+ interrupt it uses a `SharedArrayBuffer`, so it needs the page to be
147+ cross-origin isolated; on a non-isolated deployment `input()` raises
148+ ("input() is not available in this environment") rather than prompting.
148149 - **Figures are per-cell** (like inline matplotlib): each cell renders the
149150 figures its own commands produce; `hold on` does not span cells.
150151 - **Named function definitions are not supported inside cells** (a numbl
@@ -162,12 +163,14 @@ degrades to a no-op there.
162163
163164 ## Development
164165
165-Requires Python ≥ 3.9 and NodeJS ≥ 20, and `numbl >= 0.4.15` on npm — the
166-first release with the browser cancellation API (`session.interrupt()` /
167-`canInterrupt`) that cell interrupt uses. (numbl `0.4.14` added the
168-incremental `session.execute` browser API this kernel is built on.) To
169-develop against an unreleased numbl checkout, run `npm pack` there and point
170-the `numbl` dependency at the tarball.
166+Requires Python ≥ 3.9 and NodeJS ≥ 20, and `numbl >= 0.4.16` on npm — the
167+release with the browser stdin API (`onInputRequest` / `provideInput` /
168+`canInput`) behind `input()`; it also carries the `session.interrupt()` /
169+`canInterrupt` cancellation API for cell interrupt (added in 0.4.15) and the
170+incremental `session.execute` browser API this kernel is built on (0.4.14).
171+To develop against an unreleased numbl checkout, run `npm pack` there and
172+point the `numbl` dependency at the tarball (as `package.json` does while
173+0.4.16 is being published).
171174
172175 ```bash
173176 python -m venv .venv && source .venv/bin/activate
package.jsonmodified+1−1View file
@@ -55,7 +55,7 @@
5555 "@jupyterlab/rendermime-interfaces": "^3.9.0",
5656 "@jupyterlite/services": "^0.8.1",
5757 "@lumino/widgets": "^2.3.0",
58- "numbl": "^0.4.15",
58+ "numbl": "^0.4.16",
5959 "react": "^18.2.0",
6060 "react-dom": "^18.2.0"
6161 },
src/kernel.tsmodified+17−3View file
@@ -178,10 +178,21 @@ export class NumblKernel extends BaseKernel {
178178 }
179179
180180 /**
181- * Send an `input_reply` message. stdin is not supported.
181+ * Handle an `input_reply`: hand the user's line to the numbl worker, which
182+ * is blocked inside `input()` waiting for it. JupyterLite delivers
183+ * `input_reply` out of band (it bypasses the message mutex that the running
184+ * cell's `execute_request` still holds), so this arrives mid-execution as
185+ * intended. Requires cross-origin isolation; without it `input()` errors in
186+ * numbl before ever prompting, so this is never reached.
182187 */
183188 inputReply(content: KernelMessage.IInputReplyMsg['content']): void {
184- // no-op
189+ const value =
190+ 'value' in content && typeof content.value === 'string'
191+ ? content.value
192+ : '';
193+ void this._session
194+ ?.then(session => session.provideInput(value))
195+ .catch(() => undefined);
185196 }
186197
187198 async commOpen(msg: KernelMessage.ICommOpenMsg): Promise<void> {
@@ -296,7 +307,10 @@ export class NumblKernel extends BaseKernel {
296307 this._session ??= createNumblSession({
297308 onOutput: text => this.stream({ name: 'stdout', text }),
298309 onProgress: message =>
299- this.stream({ name: 'stdout', text: `[numbl] ${message}\n` })
310+ this.stream({ name: 'stdout', text: `[numbl] ${message}\n` }),
311+ // `input()` in the running cell: prompt the front end. The worker is
312+ // blocked waiting for the reply, which arrives via inputReply().
313+ onInputRequest: prompt => this.inputRequest({ prompt, password: false })
300314 });
301315 return this._session;
302316 }
yarn.lockmodified+5−5View file
@@ -3014,7 +3014,7 @@ __metadata:
30143014 eslint-config-prettier: ^8.10.0
30153015 eslint-plugin-prettier: ^5.0.0
30163016 npm-run-all2: ^7.0.1
3017- numbl: ^0.4.15
3017+ numbl: ^0.4.16
30183018 prettier: ^3.0.0
30193019 react: ^18.2.0
30203020 react-dom: ^18.2.0
@@ -4036,9 +4036,9 @@ __metadata:
40364036 languageName: node
40374037 linkType: hard
40384038
4039-"numbl@npm:^0.4.15":
4040- version: 0.4.15
4041- resolution: "numbl@npm:0.4.15"
4039+"numbl@npm:^0.4.16":
4040+ version: 0.4.16
4041+ resolution: "numbl@npm:0.4.16"
40424042 dependencies:
40434043 fflate: ^0.8.2
40444044 h5wasm: ^0.10.3
@@ -4064,7 +4064,7 @@ __metadata:
40644064 optional: true
40654065 bin:
40664066 numbl: dist-cli/cli.js
4067- checksum: dd9b5676a78add735655a3c3fdb2a0ca3f5435cdc5b90288383e2382a2deb677b4786010c27c4d0ddf7090f3bb091c0c9b948835c070428d2321e5bd30f91bb7
4067+ checksum: bd93d555af962cae504d88412a4b79bcd6dfbfafdee5889d4a515fad2b2f0cc9b7d43455821f886d3de4f715b5791b04eec01234c6399e2962934228bea040b6
40684068 languageName: node
40694069 linkType: hard
40704070