mirror of
https://github.com/python/cpython.git
synced 2025-12-08 06:10:17 +00:00
Merge 8e258ef1e9 into 7099af8f5e
This commit is contained in:
commit
0f1460b374
2 changed files with 65 additions and 124 deletions
|
|
@ -2,21 +2,17 @@
|
|||
import ast
|
||||
import asyncio
|
||||
import asyncio.tools
|
||||
import concurrent.futures
|
||||
import contextvars
|
||||
import inspect
|
||||
import os
|
||||
import site
|
||||
import sys
|
||||
import threading
|
||||
import types
|
||||
import warnings
|
||||
|
||||
from _colorize import get_theme
|
||||
from _pyrepl.console import InteractiveColoredConsole
|
||||
|
||||
from . import futures
|
||||
|
||||
|
||||
class AsyncIOInteractiveConsole(InteractiveColoredConsole):
|
||||
|
||||
|
|
@ -29,122 +25,83 @@ def __init__(self, locals, loop):
|
|||
|
||||
def runcode(self, code):
|
||||
global return_code
|
||||
future = concurrent.futures.Future()
|
||||
|
||||
def callback():
|
||||
global return_code
|
||||
global repl_future
|
||||
global keyboard_interrupted
|
||||
|
||||
repl_future = None
|
||||
keyboard_interrupted = False
|
||||
|
||||
async def callback():
|
||||
func = types.FunctionType(code, self.locals)
|
||||
try:
|
||||
coro = func()
|
||||
except SystemExit as se:
|
||||
return_code = se.code
|
||||
self.loop.stop()
|
||||
return
|
||||
except KeyboardInterrupt as ex:
|
||||
keyboard_interrupted = True
|
||||
future.set_exception(ex)
|
||||
return
|
||||
except BaseException as ex:
|
||||
future.set_exception(ex)
|
||||
return
|
||||
coro = func()
|
||||
|
||||
if not inspect.iscoroutine(coro):
|
||||
future.set_result(coro)
|
||||
return
|
||||
return coro
|
||||
return await coro
|
||||
|
||||
try:
|
||||
repl_future = self.loop.create_task(coro, context=self.context)
|
||||
futures._chain_future(repl_future, future)
|
||||
except BaseException as exc:
|
||||
future.set_exception(exc)
|
||||
|
||||
self.loop.call_soon_threadsafe(callback, context=self.context)
|
||||
|
||||
task = self.loop.create_task(callback(), context=self.context)
|
||||
try:
|
||||
return future.result()
|
||||
except SystemExit as se:
|
||||
return_code = se.code
|
||||
self.loop.stop()
|
||||
return
|
||||
return self.loop.run_until_complete(task)
|
||||
except SystemExit:
|
||||
raise
|
||||
except BaseException:
|
||||
if keyboard_interrupted:
|
||||
if not CAN_USE_PYREPL:
|
||||
self.write("\nKeyboardInterrupt\n")
|
||||
else:
|
||||
self.showtraceback()
|
||||
self.showtraceback()
|
||||
return self.STATEMENT_FAILED
|
||||
|
||||
class REPLThread(threading.Thread):
|
||||
|
||||
def run(self):
|
||||
global return_code
|
||||
def interact():
|
||||
global return_code
|
||||
|
||||
try:
|
||||
banner = (
|
||||
f'asyncio REPL {sys.version} on {sys.platform}\n'
|
||||
f'Use "await" directly instead of "asyncio.run()".\n'
|
||||
f'Type "help", "copyright", "credits" or "license" '
|
||||
f'for more information.\n'
|
||||
try:
|
||||
banner = (
|
||||
f'asyncio REPL {sys.version} on {sys.platform}\n'
|
||||
f'Use "await" directly instead of "asyncio.run()".\n'
|
||||
f'Type "help", "copyright", "credits" or "license" '
|
||||
f'for more information.\n'
|
||||
)
|
||||
|
||||
console.write(banner)
|
||||
|
||||
if startup_path := os.getenv("PYTHONSTARTUP"):
|
||||
sys.audit("cpython.run_startup", startup_path)
|
||||
|
||||
import tokenize
|
||||
with tokenize.open(startup_path) as f:
|
||||
startup_code = compile(f.read(), startup_path, "exec")
|
||||
exec(startup_code, console.locals)
|
||||
|
||||
ps1 = getattr(sys, "ps1", ">>> ")
|
||||
if CAN_USE_PYREPL:
|
||||
theme = get_theme().syntax
|
||||
ps1 = f"{theme.prompt}{ps1}{theme.reset}"
|
||||
import_line = f'{theme.keyword}import{theme.reset} asyncio'
|
||||
else:
|
||||
import_line = "import asyncio"
|
||||
console.write(f"{ps1}{import_line}\n")
|
||||
|
||||
if CAN_USE_PYREPL:
|
||||
from _pyrepl.simple_interact import (
|
||||
run_multiline_interactive_console,
|
||||
)
|
||||
|
||||
console.write(banner)
|
||||
|
||||
if startup_path := os.getenv("PYTHONSTARTUP"):
|
||||
sys.audit("cpython.run_startup", startup_path)
|
||||
|
||||
import tokenize
|
||||
with tokenize.open(startup_path) as f:
|
||||
startup_code = compile(f.read(), startup_path, "exec")
|
||||
exec(startup_code, console.locals)
|
||||
|
||||
ps1 = getattr(sys, "ps1", ">>> ")
|
||||
if CAN_USE_PYREPL:
|
||||
theme = get_theme().syntax
|
||||
ps1 = f"{theme.prompt}{ps1}{theme.reset}"
|
||||
import_line = f'{theme.keyword}import{theme.reset} asyncio'
|
||||
else:
|
||||
import_line = "import asyncio"
|
||||
console.write(f"{ps1}{import_line}\n")
|
||||
|
||||
if CAN_USE_PYREPL:
|
||||
from _pyrepl.simple_interact import (
|
||||
run_multiline_interactive_console,
|
||||
)
|
||||
try:
|
||||
sys.ps1 = ps1
|
||||
run_multiline_interactive_console(console)
|
||||
except SystemExit:
|
||||
# expected via the `exit` and `quit` commands
|
||||
pass
|
||||
except BaseException:
|
||||
# unexpected issue
|
||||
console.showtraceback()
|
||||
console.write("Internal error, ")
|
||||
return_code = 1
|
||||
else:
|
||||
try:
|
||||
sys.ps1 = ps1
|
||||
run_multiline_interactive_console(console)
|
||||
except SystemExit as se:
|
||||
# expected via the `exit` and `quit` commands
|
||||
return_code = se.code
|
||||
except BaseException:
|
||||
# unexpected issue
|
||||
console.showtraceback()
|
||||
console.write("Internal error, ")
|
||||
return_code = 1
|
||||
else:
|
||||
try:
|
||||
console.interact(banner="", exitmsg="")
|
||||
finally:
|
||||
warnings.filterwarnings(
|
||||
'ignore',
|
||||
message=r'^coroutine .* was never awaited$',
|
||||
category=RuntimeWarning)
|
||||
except SystemExit as se:
|
||||
return_code = se.code
|
||||
finally:
|
||||
warnings.filterwarnings(
|
||||
'ignore',
|
||||
message=r'^coroutine .* was never awaited$',
|
||||
category=RuntimeWarning)
|
||||
|
||||
loop.call_soon_threadsafe(loop.stop)
|
||||
|
||||
def interrupt(self) -> None:
|
||||
if not CAN_USE_PYREPL:
|
||||
return
|
||||
|
||||
from _pyrepl.simple_interact import _get_reader
|
||||
r = _get_reader()
|
||||
if r.threading_hook is not None:
|
||||
r.threading_hook.add("") # type: ignore
|
||||
loop.call_soon_threadsafe(loop.stop)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
|
@ -198,9 +155,6 @@ def interrupt(self) -> None:
|
|||
|
||||
console = AsyncIOInteractiveConsole(repl_locals, loop)
|
||||
|
||||
repl_future = None
|
||||
keyboard_interrupted = False
|
||||
|
||||
try:
|
||||
import readline # NoQA
|
||||
except ImportError:
|
||||
|
|
@ -223,21 +177,6 @@ def interrupt(self) -> None:
|
|||
completer = rlcompleter.Completer(console.locals)
|
||||
readline.set_completer(completer.complete)
|
||||
|
||||
repl_thread = REPLThread(name="Interactive thread")
|
||||
repl_thread.daemon = True
|
||||
repl_thread.start()
|
||||
|
||||
while True:
|
||||
try:
|
||||
loop.run_forever()
|
||||
except KeyboardInterrupt:
|
||||
keyboard_interrupted = True
|
||||
if repl_future and not repl_future.done():
|
||||
repl_future.cancel()
|
||||
repl_thread.interrupt()
|
||||
continue
|
||||
else:
|
||||
break
|
||||
|
||||
interact()
|
||||
console.write('exiting asyncio REPL...\n')
|
||||
sys.exit(return_code)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,2 @@
|
|||
Refactor and simplify the asyncio REPL by removing the threading and future handling.
|
||||
This fixes an issue on MacOS that the readline module will be broken after pressing Ctrl+C.
|
||||
Loading…
Add table
Add a link
Reference in a new issue