1212
1313import trio
1414import trio .lowlevel
15+ from trio ._util import final
1516
1617
18+ @final
1719class TrioInteractiveConsole (InteractiveConsole ):
1820 # code.InteractiveInterpreter defines locals as Mapping[str, Any]
1921 # but when we pass this to FunctionType it expects a dict. So
@@ -25,25 +27,32 @@ def __init__(self, repl_locals: dict[str, object] | None = None):
2527 self .compile .compiler .flags |= ast .PyCF_ALLOW_TOP_LEVEL_AWAIT
2628
2729 def runcode (self , code : types .CodeType ) -> None :
28- async def _runcode_in_trio () -> outcome .Outcome [object ]:
29- func = types .FunctionType (code , self .locals )
30- if inspect .iscoroutinefunction (func ):
31- return await outcome .acapture (func )
30+ func = types .FunctionType (code , self .locals )
31+ if inspect .iscoroutinefunction (func ):
32+ result = trio .from_thread .run (outcome .acapture , func )
33+ else :
34+ result = trio .from_thread .run_sync (outcome .capture , func )
35+ if isinstance (result , outcome .Error ):
36+ # If it is SystemExit, quit the repl. Otherwise, print the traceback.
37+ # If there is a SystemExit inside a BaseExceptionGroup, it probably isn't
38+ # the user trying to quit the repl, but rather an error in the code. So, we
39+ # don't try to inspect groups for SystemExit. Instead, we just print and
40+ # return to the REPL.
41+ if isinstance (result .error , SystemExit ):
42+ raise result .error
3243 else :
33- return outcome .capture (func )
44+ # Inline our own version of self.showtraceback that can use
45+ # outcome.Error.error directly to print clean tracebacks.
46+ # This also means overriding self.showtraceback does nothing.
47+ sys .last_type , sys .last_value = type (result .error ), result .error
48+ sys .last_traceback = result .error .__traceback__
49+ # see https://docs.python.org/3/library/sys.html#sys.last_exc
50+ if sys .version_info >= (3 , 12 ):
51+ sys .last_exc = result .error
3452
35- try :
36- trio .from_thread .run (_runcode_in_trio ).unwrap ()
37- except SystemExit :
38- # If it is SystemExit quit the repl. Otherwise, print the
39- # traceback.
40- # There could be a SystemExit inside a BaseExceptionGroup. If
41- # that happens, it probably isn't the user trying to quit the
42- # repl, but an error in the code. So we print the exception
43- # and stay in the repl.
44- raise
45- except BaseException :
46- self .showtraceback ()
53+ # We always use sys.excepthook, unlike other implementations.
54+ # This means that overriding self.write also does nothing to tbs.
55+ sys .excepthook (sys .last_type , sys .last_value , sys .last_traceback )
4756
4857
4958async def run_repl (console : TrioInteractiveConsole ) -> None :
0 commit comments