Description
It seemed a little too good to be true that Ctrl+C "just works" in the new Trio REPL (#2972, #3002), it turns out there are a couple weird problems. Interrupting running sync and async code seems to go fine, but when you are just sitting at the input prompt, KI cannot reach that code in the "usual" fashion. I've noticed 3 issues so far:
Runner._ki_pending
flag survives returning to the prompt
Consider executing a simple uninterruptible operation:
>>> await trio.to_thread.run_sync(time.sleep, 5)
This should and does last 5 seconds even if you try to interrupt it. However, Trio is still trying to get rid of the KI hot potato, so the next checkpoint will fire off a KI:
>>> await trio.sleep(0)
Traceback (most recent call last):
File "<console>", line 1, in <module>
<snip>
raise KeyboardInterrupt
KeyboardInterrupt
>>>
This one seems to be easy enough to solve, either dig into the Runner and manually reset the flag every time we're done processing input, or run trio.from_thread.run(trio.lowlevel.checkpoint_if_cancelled)
and it will safely propagate to the normal console KI code. However, after doing that exposed the next two issues.
input
on Windows sees Ctrl+C even though there is a handler AND it's not in the main thread
If you hit Ctrl+C any time the console is waiting for input, the Trio run ends:
>>> {Ctrl+C}
now exiting TrioInteractiveConsole...
Traceback (most recent call last):
<snip>
raise runner.main_task_outcome.error
KeyboardInterrupt
^C
C:\>
(Weirdly, this doesn't happen during PyCharm's terminal emulation!) This is very simply a result of input
popping out EOFError
as if you'd done Ctrl+Z+Enter:
C:\>python -c "import trio; trio.run(trio.to_thread.run_sync, input)" {then hit Ctrl+C}
Traceback (most recent call last):
<snip>
ret = context.run(sync_fn, *args)
EOFError
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
<snip>
raise runner.main_task_outcome.error
KeyboardInterrupt
C:\>
I suppose in the main thread this is overridden by the KeyboardInterrupt
at some point for the python REPL behavior to work right. This seems like a matter of transforming the Exception in our console subclass:
def raw_input(self, prompt=""):
try:
return input(prompt)
except EOFError:
# check if trio has a pending KI
trio.from_thread.run(trio.lowlevel.checkpoint_if_cancelled)
raise
This works fine as far as I can tell.
input
on Linux DOES NOT see Ctrl+C (because it's not in the main thread)
This isn't so bad as it only means KI at the input prompt doesn't take effect until you finish entering a (possibly multiline) command, and even then the command runs to completion. This is weird, but maybe tolerable? I think the worst part is that you can't quickly discard/drop out of multiline edits. Furthermore, I have no idea how to fix this.
Summary
This is an issue instead of a PR because there might be a smarter way to interact with KI that would also work on linux, and I wanted other people to follow up on this after experimenting a bit if there's any other weird behavior that isn't covered by the KI above checks (such as the linux case).