Skip to content

input(): forward prompt through Conductor and flush pending stdout - #254

Merged
martin-henz merged 8 commits into
source-academy:mainfrom
Akshay-2007-1:fix/py-slang-190-input-popup
Jul 10, 2026
Merged

martin-henz merged 8 commits into
source-academy:mainfrom
Akshay-2007-1:fix/py-slang-190-input-popup

Conversation

@Akshay-2007-1

@Akshay-2007-1 Akshay-2007-1 commented Jul 9, 2026 •

Copy link
Copy Markdown
Contributor

Summary

Closes #190 on the py-slang side. input() already blocked correctly via conductor.requestInput() (the CSE machine's async generator loop suspends naturally on that await), but two things were missing:

  • MiscBuiltins.input discarded its prompt argument entirely and called requestInput() with zero args.
  • Nothing flushed stdout buffered by earlier print()s before blocking, so context printed just before an input() call in the same chunk wouldn't reach the user until the whole chunk finished.

Changes:

  • MiscBuiltins.input now validates 0-1 args (@Validate(0, 1, "input", true)), writes the prompt to stdout before blocking (matching CPython's input(prompt), which prints the prompt with no trailing newline), and forwards it through receiveInput(context, prompt).
  • createInputStream gained a setNextPrompt side-channel so its pull()-driven ReadableStream can pass a per-call prompt to conductor.requestInput(prompt) — pull() itself has no visibility into per-call arguments.
  • receiveInput now flushes any buffered stdout (via a new optional context.streams.flushStdout hook, wired up in PyCseEvaluator.evaluateChunk) before blocking.
  • Added src/tests/input.test.ts covering both the with- and without-prompt paths.

Depends on

This depends on an unreleased Conductor protocol change adding requestInput(prompt?: string) / IHostPlugin.receiveInputRequest: source-academy/conductor#40. Until that merges and a new tag is cut, this branch points @sourceacademy/conductor at a local portal: dev resolution in package.json/yarn.lock — that needs to be repinned to the real published tag before merge, and this PR should probably stay in draft/blocked until then.

Note for reviewers: the portal: symlink currently breaks this repo's jest.config.js locally (its moduleNameMapper/transformIgnorePatterns for @sourceacademy/conductor assume a real node_modules directory, not a symlink resolving outside rootDir) — verified correctness by temporarily swapping in a physical copy of the built conductor package for test runs. This resolves itself once the portal pin is replaced with a real release.

Test plan

  • tsc --noEmit passes clean
  • jest src/tests/input.test.ts passes (2/2)
  • Full jest src/tests/stdlib.test.ts suite passes (1812/1812), confirming no regressions
  • Manual verification in a running frontend still pending (blocked on the frontend-side PR, not yet opened)

Completes the py-slang side of source-academy#190: input()
was already blocking correctly via conductor.requestInput(), but
discarded its prompt argument entirely and never flushed stdout
buffered by earlier print()s before blocking.

- MiscBuiltins.input now accepts 0-1 args (@Validate(0, 1, ...)),
  writes the prompt to stdout (matching CPython's input(prompt)
  behavior) and forwards it to receiveInput/conductor.requestInput.
- createInputStream gained a setNextPrompt side-channel so its
  pull()-driven ReadableStream can pass a per-call prompt through to
  requestInput(), since pull() itself has no visibility into
  per-call arguments.
- receiveInput now flushes any buffered stdout before blocking, so
  print()s issued earlier in the same chunk aren't stuck in the
  buffer while the popup waits.
- Context.streams gained an optional flushStdout hook, wired up in
  PyCseEvaluator.evaluateChunk.
- Added src/tests/input.test.ts covering both the with- and
  without-prompt paths.

Depends on the (as yet unpublished) conductor protocol change adding
requestInput(prompt?). Points @sourceacademy/conductor at the local
checkout via a portal: resolution for now; repin to a real published
version once that lands upstream. Note: the portal symlink currently
breaks this repo's jest.config.js (its moduleNameMapper/
transformIgnorePatterns assume a real node_modules directory, not a
symlink resolving outside rootDir) - verified tests by temporarily
copying the built conductor package into node_modules instead; no
action needed once a real release replaces the portal link.

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request adds support for prompts in the Python input() built-in function, ensuring that any buffered stdout is flushed before blocking on stdin, and passing the prompt to the conductor. A critical issue was identified in createInputStream where the default highWaterMark of 1 on ReadableStream triggers an eager pull() upon initialization, causing the first prompt to be bypassed. Setting highWaterMark: 0 is recommended to ensure input is only pulled on-demand.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment thread src/engines/cse/streams.ts Resolved
Akshay-2007-1 and others added 3 commits July 9, 2026 18:24
ReadableStream defaults to highWaterMark: 1, which makes it eagerly
call pull() as soon as it's constructed - i.e. during evaluator
init, before any Python input() call has set a prompt. That fired a
phantom conductor.requestInput(undefined) and left each real prompt
to be consumed one input() call late (off-by-one).

Setting highWaterMark: 0 makes pull() only run in direct response to
reader.read(), matching the on-demand behavior the code already
assumed. Caught by gemini-code-assist review on PR source-academy#254; added a
regression test using the real createInputStream (the existing
input.test.ts mocked stdin directly and couldn't have caught this).
@Akshay-2007-1

Copy link
Copy Markdown
Contributor Author

@coderabbitai, any review?

@coderabbitai

coderabbitai Bot commented Jul 9, 2026 •

Copy link
Copy Markdown

@Akshay-2007-1 Sure, I'll take a look now. Note that the PR description mentions this depends on an unreleased Conductor change (source-academy/conductor#40) and currently points @sourceacademy/conductor at a local portal: dev resolution that needs to be repinned before merge — I'll flag anything related to that as well, but you'll want to repin before this is mergeable regardless of the review outcome.

✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@Akshay-2007-1
Akshay-2007-1 marked this pull request as draft July 9, 2026 13:13
@coderabbitai

coderabbitai Bot commented Jul 9, 2026

Copy link
Copy Markdown

Review Change Stack

Walkthrough

Adds prompt support to the Python input() builtin: a new InputStreamContext type with setNextPrompt, updated createInputStream/receiveInput sequencing that flushes stdout before requesting input, wiring of flushStdout through evaluator and runner streams, updated input() builtin behavior, new tests, and an unrelated package.json dependency resolution override.

Changes

Prompt-aware input() flow

Layer / File(s) Summary
InputStreamContext type and Context streams contract
src/engines/cse/context.ts, src/engines/cse/streams.ts
Context.streams.stdin now uses InputStreamContext with an optional flushStdout field; InputStreamContext adds setNextPrompt, and createInputStream tracks pendingPrompt with highWaterMark: 0 to avoid phantom pulls.
receiveInput sequencing and input() builtin
src/engines/cse/streams.ts, src/stdlib/misc.ts
receiveInput accepts an optional prompt, flushes stdout, then sets the stdin prompt before reading; MiscBuiltins.input validates 0–1 args, prints the prompt without a newline, and forwards it to receiveInput.
Evaluator and runner stream wiring
src/conductor/PyCseEvaluator.ts, src/runner.ts
PyCseEvaluator wires flushStdout to flushOutput(conductor); makeMemoryStreams adds a setNextPrompt no-op on stdin.
Input prompt tests and mocks
src/tests/input.test.ts, src/tests/utils.ts
New tests cover prompt/no-prompt input() behavior and prompt ordering via real createInputStream/receiveInput; the shared test stdin mock gains setNextPrompt.

Dependency Resolution Override

Layer / File(s) Summary
package.json resolutions override
package.json
Adds a resolutions block forcing @sourceacademy/conductor to a local portal path.

Estimated code review effort: 3 (Moderate) | ~25 minutes

Sequence Diagram(s)

sequenceDiagram
  participant PythonProgram
  participant MiscBuiltinsInput
  participant ReceiveInput
  participant StdinStream
  participant Conductor

  PythonProgram->>MiscBuiltinsInput: input(prompt)
  MiscBuiltinsInput->>MiscBuiltinsInput: displayOutput(prompt)
  MiscBuiltinsInput->>ReceiveInput: receiveInput(context, prompt)
  ReceiveInput->>ReceiveInput: flushStdout()
  ReceiveInput->>StdinStream: setNextPrompt(prompt)
  StdinStream->>Conductor: requestInput(pendingPrompt)
  Conductor-->>StdinStream: response
  StdinStream-->>ReceiveInput: value
  ReceiveInput-->>MiscBuiltinsInput: typed value
Loading

Poem

A rabbit hops to fetch a line,
"Write your string here:" — right on time!
Stdout flushed before I wait,
No more hanging at the gate. 🐰
Prompt by prompt, in order true,
Hop, hop, input()— all for you!

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Out of Scope Changes check ⚠️ Warning The temporary portal resolution in package.json is unrelated to the input() fix and appears to be a merge-blocking dev pin. Remove the local portal resolution and repin @sourceacademy/conductor to the intended published release before merging.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the main feature fix: forwarding prompts through Conductor and flushing pending stdout.
Description check ✅ Passed The description is directly related to the changeset and explains the input prompt and stdout flush fixes.
Linked Issues check ✅ Passed The changes implement the reported input() behavior fix, including prompt handling, stdout flushing, and regression tests.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 ESLint

If the error stems from missing dependencies, add them to the package.json file. For unrecoverable errors (e.g., due to private dependencies), disable the tool in the CodeRabbit configuration.

ESLint install failed. For unrecoverable errors, disable the tool in CodeRabbit configuration.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@package.json`:
- Around line 85-87: The package.json resolutions block still pins
`@sourceacademy/conductor` to a machine-specific portal:/home/vakshay/conductor
path, which must be removed before merge. Update the resolutions entry in
package.json to point to a published release or a git URL pinned to a specific
commit/tag, using the `@sourceacademy/conductor` resolution key so the project
builds reproducibly in CI and for other developers.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 04140fee-f461-466b-b00e-f2c8246f2735

📥 Commits

Reviewing files that changed from the base of the PR and between 738e518 and c5a782b.

⛔ Files ignored due to path filters (1)
  • yarn.lock is excluded by !**/yarn.lock, !**/*.lock
📒 Files selected for processing (8)
  • package.json
  • src/conductor/PyCseEvaluator.ts
  • src/engines/cse/context.ts
  • src/engines/cse/streams.ts
  • src/runner.ts
  • src/stdlib/misc.ts
  • src/tests/input.test.ts
  • src/tests/utils.ts

Comment thread package.json Resolved
martin-henz and others added 4 commits July 9, 2026 22:57
source-academy/conductor#40 merged and got tagged 0.6.0
(e105c9c), so the temporary portal: dev pin isn't needed anymore.
Verified: tsc --noEmit clean, input.test.ts (3/3) and the full
stdlib.test.ts suite (1812/1812) all pass against the real published
dependency.
- requestInput mock in the createInputStream test was declared async
  with no await inside, tripping @typescript-eslint/require-await.
  It does no actual async work, so drop async and return
  Promise.resolve(...) directly.
- Replace two `{} as any` dummy ExprNS.Call args (each with an
  eslint-disable comment that wasn't actually suppressing anything)
  with `{} as ExprNS.Call`, matching the real parameter type.
@Akshay-2007-1
Akshay-2007-1 marked this pull request as ready for review July 10, 2026 05:54
@Akshay-2007-1 Akshay-2007-1 self-assigned this Jul 10, 2026

@martin-henz martin-henz left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good. This is a nice example for using the channel communication between an evaluator and the frontend.

@martin-henz
martin-henz merged commit a398b31 into source-academy:main Jul 10, 2026
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

CSE Machine: viz and non-viz: input function not working

2 participants