forked from python/cpython
-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
pythongh-120221: Support KeyboardInterrupt in asyncio REPL
This switches the main pyrepl event loop to always be non-blocking so that it can listen to incoming interruptions from other threads. This also resolves invalid display of exceptions from other threads (pythongh-123178).
- Loading branch information
Showing
4 changed files
with
113 additions
and
15 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,74 @@ | ||
from __future__ import annotations | ||
|
||
from dataclasses import dataclass, field | ||
import traceback | ||
|
||
|
||
TYPE_CHECKING = False | ||
if TYPE_CHECKING: | ||
from threading import Thread | ||
from types import TracebackType | ||
from typing import Protocol | ||
|
||
class ExceptHookArgs(Protocol): | ||
@property | ||
def exc_type(self) -> type[BaseException]: ... | ||
@property | ||
def exc_value(self) -> BaseException | None: ... | ||
@property | ||
def exc_traceback(self) -> TracebackType | None: ... | ||
@property | ||
def thread(self) -> Thread | None: ... | ||
|
||
class ShowExceptions(Protocol): | ||
def __call__(self) -> int: ... | ||
def add(self, s: str) -> None: ... | ||
|
||
from .reader import Reader | ||
|
||
|
||
def install_threading_hook(reader: Reader) -> None: | ||
import threading | ||
|
||
@dataclass | ||
class ExceptHookHandler: | ||
lock: threading.Lock = field(default_factory=threading.Lock) | ||
messages: list[str] = field(default_factory=list) | ||
|
||
def show(self) -> int: | ||
count = 0 | ||
with self.lock: | ||
if not self.messages: | ||
return 0 | ||
reader.restore() | ||
for tb in self.messages: | ||
count += 1 | ||
if tb: | ||
print(tb) | ||
self.messages.clear() | ||
reader.scheduled_commands.append("ctrl-c") | ||
reader.prepare() | ||
return count | ||
|
||
def add(self, s: str) -> None: | ||
with self.lock: | ||
self.messages.append(s) | ||
|
||
def exception(self, args: ExceptHookArgs) -> None: | ||
lines = traceback.format_exception( | ||
args.exc_type, | ||
args.exc_value, | ||
args.exc_traceback, | ||
colorize=reader.can_colorize, | ||
) # type: ignore[call-overload] | ||
pre = f"\nException in {args.thread.name}:\n" if args.thread else "\n" | ||
tb = pre + "".join(lines) | ||
self.add(tb) | ||
|
||
def __call__(self) -> int: | ||
return self.show() | ||
|
||
|
||
handler = ExceptHookHandler() | ||
reader.threading_hook = handler | ||
threading.excepthook = handler.exception |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters