Skip to content

Ctrl+C behavior problems at the new REPL #3007

Open
@richardsheridan

Description

@richardsheridan

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).

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions