11import ast
22import asyncio
3- import code
43import concurrent .futures
54import inspect
5+ import os
66import site
77import sys
88import threading
99import types
1010import warnings
1111
12+ from _colorize import can_colorize , ANSIColors # type: ignore[import-not-found]
13+ from _pyrepl .console import InteractiveColoredConsole
14+
1215from . import futures
1316
1417
15- class AsyncIOInteractiveConsole (code . InteractiveConsole ):
18+ class AsyncIOInteractiveConsole (InteractiveColoredConsole ):
1619
1720 def __init__ (self , locals , loop ):
18- super ().__init__ (locals )
21+ super ().__init__ (locals , filename = "<stdin>" )
1922 self .compile .compiler .flags |= ast .PyCF_ALLOW_TOP_LEVEL_AWAIT
2023
2124 self .loop = loop
2225
2326 def runcode (self , code ):
27+ global return_code
2428 future = concurrent .futures .Future ()
2529
2630 def callback ():
31+ global return_code
2732 global repl_future
28- global repl_future_interrupted
33+ global keyboard_interrupted
2934
3035 repl_future = None
31- repl_future_interrupted = False
36+ keyboard_interrupted = False
3237
3338 func = types .FunctionType (code , self .locals )
3439 try :
3540 coro = func ()
36- except SystemExit :
37- raise
41+ except SystemExit as se :
42+ return_code = se .code
43+ self .loop .stop ()
44+ return
3845 except KeyboardInterrupt as ex :
39- repl_future_interrupted = True
46+ keyboard_interrupted = True
4047 future .set_exception (ex )
4148 return
4249 except BaseException as ex :
@@ -57,10 +64,12 @@ def callback():
5764
5865 try :
5966 return future .result ()
60- except SystemExit :
61- raise
67+ except SystemExit as se :
68+ return_code = se .code
69+ self .loop .stop ()
70+ return
6271 except BaseException :
63- if repl_future_interrupted :
72+ if keyboard_interrupted :
6473 self .write ("\n KeyboardInterrupt\n " )
6574 else :
6675 self .showtraceback ()
@@ -69,18 +78,56 @@ def callback():
6978class REPLThread (threading .Thread ):
7079
7180 def run (self ):
81+ global return_code
82+
7283 try :
7384 banner = (
7485 f'asyncio REPL { sys .version } on { sys .platform } \n '
7586 f'Use "await" directly instead of "asyncio.run()".\n '
7687 f'Type "help", "copyright", "credits" or "license" '
7788 f'for more information.\n '
78- f'{ getattr (sys , "ps1" , ">>> " )} import asyncio'
7989 )
8090
81- console .interact (
82- banner = banner ,
83- exitmsg = 'exiting asyncio REPL...' )
91+ console .write (banner )
92+
93+ if startup_path := os .getenv ("PYTHONSTARTUP" ):
94+ import tokenize
95+ with tokenize .open (startup_path ) as f :
96+ startup_code = compile (f .read (), startup_path , "exec" )
97+ exec (startup_code , console .locals )
98+
99+ ps1 = getattr (sys , "ps1" , ">>> " )
100+ if can_colorize ():
101+ ps1 = f"{ ANSIColors .BOLD_MAGENTA } { ps1 } { ANSIColors .RESET } "
102+ console .write (f"{ ps1 } import asyncio\n " )
103+
104+ try :
105+ import errno
106+ if os .getenv ("PYTHON_BASIC_REPL" ):
107+ raise RuntimeError ("user environment requested basic REPL" )
108+ if not os .isatty (sys .stdin .fileno ()):
109+ raise OSError (errno .ENOTTY , "tty required" , "stdin" )
110+
111+ # This import will fail on operating systems with no termios.
112+ from _pyrepl .simple_interact import (
113+ check ,
114+ run_multiline_interactive_console ,
115+ )
116+ if err := check ():
117+ raise RuntimeError (err )
118+ except Exception as e :
119+ console .interact (banner = "" , exitmsg = exit_message )
120+ else :
121+ try :
122+ run_multiline_interactive_console (console = console )
123+ except SystemExit :
124+ # expected via the `exit` and `quit` commands
125+ pass
126+ except BaseException :
127+ # unexpected issue
128+ console .showtraceback ()
129+ console .write ("Internal error, " )
130+ return_code = 1
84131 finally :
85132 warnings .filterwarnings (
86133 'ignore' ,
@@ -91,6 +138,9 @@ def run(self):
91138
92139
93140if __name__ == '__main__' :
141+ CAN_USE_PYREPL = True
142+
143+ return_code = 0
94144 loop = asyncio .new_event_loop ()
95145 asyncio .set_event_loop (loop )
96146
@@ -103,7 +153,7 @@ def run(self):
103153 console = AsyncIOInteractiveConsole (repl_locals , loop )
104154
105155 repl_future = None
106- repl_future_interrupted = False
156+ keyboard_interrupted = False
107157
108158 try :
109159 import readline # NoQA
@@ -126,17 +176,20 @@ def run(self):
126176 completer = rlcompleter .Completer (console .locals )
127177 readline .set_completer (completer .complete )
128178
129- repl_thread = REPLThread ()
179+ repl_thread = REPLThread (name = "Interactive thread" )
130180 repl_thread .daemon = True
131181 repl_thread .start ()
132182
133183 while True :
134184 try :
135185 loop .run_forever ()
136186 except KeyboardInterrupt :
187+ keyboard_interrupted = True
137188 if repl_future and not repl_future .done ():
138189 repl_future .cancel ()
139- repl_future_interrupted = True
140190 continue
141191 else :
142192 break
193+
194+ console .write ('exiting asyncio REPL...\n ' )
195+ sys .exit (return_code )
0 commit comments