| 45 | self.interrupted = False |
| 46 | |
| 47 | def runcode(self, code: CodeType) -> None: |
| 48 | func = FunctionType(code, self.locals) |
| 49 | if inspect.iscoroutinefunction(func): |
| 50 | result = trio.from_thread.run(outcome.acapture, func) |
| 51 | else: |
| 52 | result = trio.from_thread.run_sync(outcome.capture, func) |
| 53 | if isinstance(result, outcome.Error): |
| 54 | # If it is SystemExit, quit the repl. Otherwise, print the traceback. |
| 55 | # If there is a SystemExit inside a BaseExceptionGroup, it probably isn't |
| 56 | # the user trying to quit the repl, but rather an error in the code. So, we |
| 57 | # don't try to inspect groups for SystemExit. Instead, we just print and |
| 58 | # return to the REPL. |
| 59 | if isinstance(result.error, SystemExit): |
| 60 | raise result.error |
| 61 | else: |
| 62 | # Inline our own version of self.showtraceback that can use |
| 63 | # outcome.Error.error directly to print clean tracebacks. |
| 64 | # This also means overriding self.showtraceback does nothing. |
| 65 | sys.last_type, sys.last_value = type(result.error), result.error |
| 66 | sys.last_traceback = result.error.__traceback__ |
| 67 | # see https://docs.python.org/3/library/sys.html#sys.last_exc |
| 68 | if sys.version_info >= (3, 12): |
| 69 | sys.last_exc = result.error |
| 70 | |
| 71 | # We always use sys.excepthook, unlike other implementations. |
| 72 | # This means that overriding self.write also does nothing to tbs. |
| 73 | sys.excepthook(sys.last_type, sys.last_value, sys.last_traceback) |
| 74 | # clear any residual KI |
| 75 | trio.from_thread.run(trio.lowlevel.checkpoint_if_cancelled) |
| 76 | # trio.from_thread.check_cancelled() has too long of a memory |
| 77 | |
| 78 | if sys.platform == "win32": # TODO: test this line |
| 79 | |