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 ast
|
||||||
import asyncio
|
import asyncio
|
||||||
import asyncio.tools
|
import asyncio.tools
|
||||||
import concurrent.futures
|
|
||||||
import contextvars
|
import contextvars
|
||||||
import inspect
|
import inspect
|
||||||
import os
|
import os
|
||||||
import site
|
import site
|
||||||
import sys
|
import sys
|
||||||
import threading
|
|
||||||
import types
|
import types
|
||||||
import warnings
|
import warnings
|
||||||
|
|
||||||
from _colorize import get_theme
|
from _colorize import get_theme
|
||||||
from _pyrepl.console import InteractiveColoredConsole
|
from _pyrepl.console import InteractiveColoredConsole
|
||||||
|
|
||||||
from . import futures
|
|
||||||
|
|
||||||
|
|
||||||
class AsyncIOInteractiveConsole(InteractiveColoredConsole):
|
class AsyncIOInteractiveConsole(InteractiveColoredConsole):
|
||||||
|
|
||||||
|
|
@ -29,122 +25,83 @@ def __init__(self, locals, loop):
|
||||||
|
|
||||||
def runcode(self, code):
|
def runcode(self, code):
|
||||||
global return_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)
|
func = types.FunctionType(code, self.locals)
|
||||||
try:
|
coro = func()
|
||||||
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
|
|
||||||
|
|
||||||
if not inspect.iscoroutine(coro):
|
if not inspect.iscoroutine(coro):
|
||||||
future.set_result(coro)
|
return coro
|
||||||
return
|
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:
|
try:
|
||||||
return future.result()
|
return self.loop.run_until_complete(task)
|
||||||
except SystemExit as se:
|
except SystemExit:
|
||||||
return_code = se.code
|
raise
|
||||||
self.loop.stop()
|
|
||||||
return
|
|
||||||
except BaseException:
|
except BaseException:
|
||||||
if keyboard_interrupted:
|
self.showtraceback()
|
||||||
if not CAN_USE_PYREPL:
|
|
||||||
self.write("\nKeyboardInterrupt\n")
|
|
||||||
else:
|
|
||||||
self.showtraceback()
|
|
||||||
return self.STATEMENT_FAILED
|
return self.STATEMENT_FAILED
|
||||||
|
|
||||||
class REPLThread(threading.Thread):
|
|
||||||
|
|
||||||
def run(self):
|
def interact():
|
||||||
global return_code
|
global return_code
|
||||||
|
|
||||||
try:
|
try:
|
||||||
banner = (
|
banner = (
|
||||||
f'asyncio REPL {sys.version} on {sys.platform}\n'
|
f'asyncio REPL {sys.version} on {sys.platform}\n'
|
||||||
f'Use "await" directly instead of "asyncio.run()".\n'
|
f'Use "await" directly instead of "asyncio.run()".\n'
|
||||||
f'Type "help", "copyright", "credits" or "license" '
|
f'Type "help", "copyright", "credits" or "license" '
|
||||||
f'for more information.\n'
|
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,
|
||||||
)
|
)
|
||||||
|
try:
|
||||||
console.write(banner)
|
sys.ps1 = ps1
|
||||||
|
run_multiline_interactive_console(console)
|
||||||
if startup_path := os.getenv("PYTHONSTARTUP"):
|
except SystemExit as se:
|
||||||
sys.audit("cpython.run_startup", startup_path)
|
# expected via the `exit` and `quit` commands
|
||||||
|
return_code = se.code
|
||||||
import tokenize
|
except BaseException:
|
||||||
with tokenize.open(startup_path) as f:
|
# unexpected issue
|
||||||
startup_code = compile(f.read(), startup_path, "exec")
|
console.showtraceback()
|
||||||
exec(startup_code, console.locals)
|
console.write("Internal error, ")
|
||||||
|
return_code = 1
|
||||||
ps1 = getattr(sys, "ps1", ">>> ")
|
else:
|
||||||
if CAN_USE_PYREPL:
|
try:
|
||||||
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:
|
|
||||||
console.interact(banner="", exitmsg="")
|
console.interact(banner="", exitmsg="")
|
||||||
finally:
|
except SystemExit as se:
|
||||||
warnings.filterwarnings(
|
return_code = se.code
|
||||||
'ignore',
|
finally:
|
||||||
message=r'^coroutine .* was never awaited$',
|
warnings.filterwarnings(
|
||||||
category=RuntimeWarning)
|
'ignore',
|
||||||
|
message=r'^coroutine .* was never awaited$',
|
||||||
|
category=RuntimeWarning)
|
||||||
|
|
||||||
loop.call_soon_threadsafe(loop.stop)
|
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
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
|
|
@ -198,9 +155,6 @@ def interrupt(self) -> None:
|
||||||
|
|
||||||
console = AsyncIOInteractiveConsole(repl_locals, loop)
|
console = AsyncIOInteractiveConsole(repl_locals, loop)
|
||||||
|
|
||||||
repl_future = None
|
|
||||||
keyboard_interrupted = False
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
import readline # NoQA
|
import readline # NoQA
|
||||||
except ImportError:
|
except ImportError:
|
||||||
|
|
@ -223,21 +177,6 @@ def interrupt(self) -> None:
|
||||||
completer = rlcompleter.Completer(console.locals)
|
completer = rlcompleter.Completer(console.locals)
|
||||||
readline.set_completer(completer.complete)
|
readline.set_completer(completer.complete)
|
||||||
|
|
||||||
repl_thread = REPLThread(name="Interactive thread")
|
interact()
|
||||||
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
|
|
||||||
|
|
||||||
console.write('exiting asyncio REPL...\n')
|
console.write('exiting asyncio REPL...\n')
|
||||||
sys.exit(return_code)
|
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