Skip to content

Commit efb40f7

Browse files
committed
refactor: Simplify asyncio REPL code by removing threading and future handling
Signed-off-by: Frost Ming <[email protected]>
1 parent 9c18f75 commit efb40f7

File tree

1 file changed

+63
-124
lines changed

1 file changed

+63
-124
lines changed

Lib/asyncio/__main__.py

Lines changed: 63 additions & 124 deletions
Original file line numberDiff line numberDiff line change
@@ -2,21 +2,17 @@
22
import ast
33
import asyncio
44
import asyncio.tools
5-
import concurrent.futures
65
import contextvars
76
import inspect
87
import os
98
import site
109
import sys
11-
import threading
1210
import types
1311
import warnings
1412

1513
from _colorize import get_theme
1614
from _pyrepl.console import InteractiveColoredConsole
1715

18-
from . import futures
19-
2016

2117
class AsyncIOInteractiveConsole(InteractiveColoredConsole):
2218

@@ -29,122 +25,83 @@ def __init__(self, locals, loop):
2925

3026
def runcode(self, code):
3127
global return_code
32-
future = concurrent.futures.Future()
33-
34-
def callback():
35-
global return_code
36-
global repl_future
37-
global keyboard_interrupted
38-
39-
repl_future = None
40-
keyboard_interrupted = False
4128

29+
async def callback():
4230
func = types.FunctionType(code, self.locals)
43-
try:
44-
coro = func()
45-
except SystemExit as se:
46-
return_code = se.code
47-
self.loop.stop()
48-
return
49-
except KeyboardInterrupt as ex:
50-
keyboard_interrupted = True
51-
future.set_exception(ex)
52-
return
53-
except BaseException as ex:
54-
future.set_exception(ex)
55-
return
31+
coro = func()
5632

5733
if not inspect.iscoroutine(coro):
58-
future.set_result(coro)
59-
return
60-
61-
try:
62-
repl_future = self.loop.create_task(coro, context=self.context)
63-
futures._chain_future(repl_future, future)
64-
except BaseException as exc:
65-
future.set_exception(exc)
34+
return coro
35+
return await coro
6636

67-
self.loop.call_soon_threadsafe(callback, context=self.context)
6837

38+
task = self.loop.create_task(callback(), context=self.context)
6939
try:
70-
return future.result()
71-
except SystemExit as se:
72-
return_code = se.code
73-
self.loop.stop()
74-
return
40+
return self.loop.run_until_complete(task)
41+
except SystemExit:
42+
raise
7543
except BaseException:
76-
if keyboard_interrupted:
77-
if not CAN_USE_PYREPL:
78-
self.write("\nKeyboardInterrupt\n")
79-
else:
80-
self.showtraceback()
44+
self.showtraceback()
8145
return self.STATEMENT_FAILED
8246

83-
class REPLThread(threading.Thread):
8447

85-
def run(self):
86-
global return_code
48+
def interact():
49+
global return_code
8750

88-
try:
89-
banner = (
90-
f'asyncio REPL {sys.version} on {sys.platform}\n'
91-
f'Use "await" directly instead of "asyncio.run()".\n'
92-
f'Type "help", "copyright", "credits" or "license" '
93-
f'for more information.\n'
94-
)
51+
try:
52+
banner = (
53+
f'asyncio REPL {sys.version} on {sys.platform}\n'
54+
f'Use "await" directly instead of "asyncio.run()".\n'
55+
f'Type "help", "copyright", "credits" or "license" '
56+
f'for more information.\n'
57+
)
58+
59+
console.write(banner)
60+
61+
if startup_path := os.getenv("PYTHONSTARTUP"):
62+
sys.audit("cpython.run_startup", startup_path)
63+
64+
import tokenize
65+
with tokenize.open(startup_path) as f:
66+
startup_code = compile(f.read(), startup_path, "exec")
67+
exec(startup_code, console.locals)
68+
69+
ps1 = getattr(sys, "ps1", ">>> ")
70+
if CAN_USE_PYREPL:
71+
theme = get_theme().syntax
72+
ps1 = f"{theme.prompt}{ps1}{theme.reset}"
73+
import_line = f'{theme.keyword}import{theme.reset} asyncio'
74+
else:
75+
import_line = "import asyncio"
76+
console.write(f"{ps1}{import_line}\n")
9577

96-
console.write(banner)
97-
98-
if startup_path := os.getenv("PYTHONSTARTUP"):
99-
sys.audit("cpython.run_startup", startup_path)
100-
101-
import tokenize
102-
with tokenize.open(startup_path) as f:
103-
startup_code = compile(f.read(), startup_path, "exec")
104-
exec(startup_code, console.locals)
105-
106-
ps1 = getattr(sys, "ps1", ">>> ")
107-
if CAN_USE_PYREPL:
108-
theme = get_theme().syntax
109-
ps1 = f"{theme.prompt}{ps1}{theme.reset}"
110-
import_line = f'{theme.keyword}import{theme.reset} asyncio'
111-
else:
112-
import_line = "import asyncio"
113-
console.write(f"{ps1}{import_line}\n")
114-
115-
if CAN_USE_PYREPL:
116-
from _pyrepl.simple_interact import (
117-
run_multiline_interactive_console,
118-
)
119-
try:
120-
sys.ps1 = ps1
121-
run_multiline_interactive_console(console)
122-
except SystemExit:
123-
# expected via the `exit` and `quit` commands
124-
pass
125-
except BaseException:
126-
# unexpected issue
127-
console.showtraceback()
128-
console.write("Internal error, ")
129-
return_code = 1
130-
else:
78+
if CAN_USE_PYREPL:
79+
from _pyrepl.simple_interact import (
80+
run_multiline_interactive_console,
81+
)
82+
try:
83+
sys.ps1 = ps1
84+
run_multiline_interactive_console(console)
85+
except SystemExit as se:
86+
# expected via the `exit` and `quit` commands
87+
return_code = se.code
88+
except BaseException:
89+
# unexpected issue
90+
console.showtraceback()
91+
console.write("Internal error, ")
92+
return_code = 1
93+
else:
94+
try:
13195
console.interact(banner="", exitmsg="")
132-
finally:
133-
warnings.filterwarnings(
134-
'ignore',
135-
message=r'^coroutine .* was never awaited$',
136-
category=RuntimeWarning)
137-
138-
loop.call_soon_threadsafe(loop.stop)
139-
140-
def interrupt(self) -> None:
141-
if not CAN_USE_PYREPL:
142-
return
96+
except SystemExit as se:
97+
return_code = se.code
98+
finally:
99+
warnings.filterwarnings(
100+
'ignore',
101+
message=r'^coroutine .* was never awaited$',
102+
category=RuntimeWarning)
143103

144-
from _pyrepl.simple_interact import _get_reader
145-
r = _get_reader()
146-
if r.threading_hook is not None:
147-
r.threading_hook.add("") # type: ignore
104+
loop.call_soon_threadsafe(loop.stop)
148105

149106

150107
if __name__ == '__main__':
@@ -198,9 +155,6 @@ def interrupt(self) -> None:
198155

199156
console = AsyncIOInteractiveConsole(repl_locals, loop)
200157

201-
repl_future = None
202-
keyboard_interrupted = False
203-
204158
try:
205159
import readline # NoQA
206160
except ImportError:
@@ -223,21 +177,6 @@ def interrupt(self) -> None:
223177
completer = rlcompleter.Completer(console.locals)
224178
readline.set_completer(completer.complete)
225179

226-
repl_thread = REPLThread(name="Interactive thread")
227-
repl_thread.daemon = True
228-
repl_thread.start()
229-
230-
while True:
231-
try:
232-
loop.run_forever()
233-
except KeyboardInterrupt:
234-
keyboard_interrupted = True
235-
if repl_future and not repl_future.done():
236-
repl_future.cancel()
237-
repl_thread.interrupt()
238-
continue
239-
else:
240-
break
241-
180+
interact()
242181
console.write('exiting asyncio REPL...\n')
243182
sys.exit(return_code)

0 commit comments

Comments
 (0)