Skip to content

Fix ~30s hang on unclosed blocks (function/if/for/... with no end) - #60

Closed
mottelet wants to merge 1 commit into
Calysto:masterfrom
mottelet:fix-continuation-prompt-hang
Closed

mottelet wants to merge 1 commit into
Calysto:masterfrom
mottelet:fix-continuation-prompt-hang

Conversation

@mottelet

Copy link
Copy Markdown
Contributor

Summary

Running a cell that leaves a block unclosed — function x=f(y) with no endfunction, an if/for/while/select/try with no matching end — left Scilab sitting at its continuation prompt waiting for the rest of the block, and the kernel just hung for a long time.

metakernel's REPLWrapper actually detects this already (fast, well under a second) and has a built-in recovery path: send Ctrl-C, wait for a normal prompt, then raise a clean, catchable ValueError. But scilab-adv-cli does not respond to SIGINT while waiting for more input, so that recovery wait always ran out its full budget (pexpect's default of 30s) before giving up — so every incomplete cell cost a good ~30 seconds before showing a generic "Timed out" error, even though the session did eventually recover on its own (confirmed: the next cell executes normally afterward).

Fixed with a small REPLWrapper subclass (_ScilabREPLWrapper) that overrides just the continuation-prompt recovery step. A bare end reliably closes any Scilab block type (function/if/for/while/...) and returns to the top-level prompt in well under a second — verified directly against a live Scilab process for each block type. Nested unclosed blocks (e.g. an unclosed if containing an unclosed for) need one end per level, so it's sent repeatedly (capped at 50 attempts) until a normal prompt reappears — matching what a user closing the blocks by hand would have to type anyway.

Everything else about metakernel's flow is unchanged — the "soft continuation" empty-line retry, the eventual ValueError, and its message ("Continuation prompt found - input was incomplete:\n<code>") are all metakernel's own, already well-suited to surfacing a clear error to the user; this only fixes the part that was actually broken for Scilab specifically.

Also includes the same pre-existing ruff cleanup as #58/#59 (this branch was cut from master, which still has that debt).

Test plan

  • Verified against a live kernel: unclosed function/if/for now each report the error in ~0.2-0.3s (down from 30s+).
  • A nested unclosed if+for needs and gets two ends, also recovering in ~0.3s.
  • Normal complete multi-line blocks (if/end, function/endfunction, for/end) are unaffected and execute correctly.
  • The session remains fully functional for subsequent cells after every incomplete-input case (confirmed via disp(...) and %plot both working right after).
  • ruff check . and py_compile clean on all changed files.

🤖 Generated with Claude Code

Running a cell that leaves a block unclosed -- function x=f(y) with no
endfunction, an if/for/while/select/try with no matching end -- left
Scilab sitting at its continuation prompt waiting for the rest of the
block, and the kernel just hung. metakernel's REPLWrapper does detect
this (fast, well under a second) and has a built-in recovery: send
Ctrl-C, wait for a normal prompt, then raise a clean, catchable
ValueError. But scilab-adv-cli does not respond to SIGINT while
waiting for more input, so that recovery wait always ran out its full
budget (pexpect's default of 30s) before giving up -- so every
incomplete cell cost a good 30 seconds before showing a generic
"Timed out" error, though the session did eventually recover on its
own (confirmed: the next cell executes normally afterward).

Fixed with a small REPLWrapper subclass (_ScilabREPLWrapper) that
overrides just the continuation-prompt recovery step. A bare "end"
reliably closes any Scilab block type (function/if/for/while/...) and
returns to the top-level prompt in well under a second -- verified
directly against a live Scilab process for each block type. Nested
unclosed blocks (e.g. an unclosed if containing an unclosed for) need
one "end" per level, so it's sent repeatedly (capped at 50 attempts)
until a normal prompt reappears, matching what a user closing the
blocks by hand would have to type anyway.

Everything else about metakernel's flow is unchanged -- the "soft
continuation" empty-line retry, the eventual ValueError, and its
message ("Continuation prompt found - input was incomplete:\n<code>")
are all metakernel's own, already well-suited to surfacing a clear
error to the user; this only fixes the part that was actually broken
for Scilab specifically.

Verified against a live kernel: unclosed function/if/for now each
report the error in ~0.2-0.3s (down from 30s+), a nested unclosed
if+for needs and gets two "end"s and also recovers in ~0.3s, normal
complete multi-line blocks (if/end, function/endfunction, for/end)
are unaffected and execute correctly, and the session remains fully
functional for subsequent cells in every case.

Also includes the same pre-existing ruff cleanup as Calysto#58/Calysto#59 (this
branch was cut from master, which still has that debt).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@mottelet

Copy link
Copy Markdown
Contributor Author

Merged into #58 so all three fixes land together — closing this one in favor of that. See #58 for the combined changes and updated description.

@mottelet mottelet closed this Sep 24, 2026
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.

1 participant