-
-
Notifications
You must be signed in to change notification settings - Fork 33.1k
Description
Bug report
Bug description:
During helping triaging #140002, I've learned that asyncio incorrectly calls code.InteractiveColoredConsole.runsource
, which is reserved for the new REPL only, because the input is expected to be complete. This results in the asyncio REPL mishandling multi-line input:
❯ PYTHON_BASIC_REPL=1 ./python -m asyncio
asyncio REPL 3.15.0a0 (heads/main:5f91d5d9a41, Oct 12 2025, 22:21:59) [GCC 15.2.1 20250808 (Red Hat 15.2.1-1)] on linux
Use "await" directly instead of "asyncio.run()".
Type "help", "copyright", "credits" or "license" for more information.
>>> import asyncio
>>> async def foo():
File "<stdin>", line 1
async def foo():
^
IndentationError: expected an indented block after function definition on line 1
We may as well make stdin non-interactive (which forces the basic REPL, the new REPL requires a TTY) and reproduce with a bash one-liner:
❯ ./python -m asyncio <<< $'async def foo():\n print("Hello!")\n\nawait foo()'
asyncio REPL 3.15.0a0 (heads/main:5f91d5d9a41, Oct 12 2025, 22:21:59) [GCC 15.2.1 20250808 (Red Hat 15.2.1-1)] on linux
Use "await" directly instead of "asyncio.run()".
Type "help", "copyright", "credits" or "license" for more information.
>>> import asyncio
>>> File "<stdin>", line 1
async def foo():
^
IndentationError: expected an indented block after function definition on line 1
>>> File "<stdin>", line 1
print("Hello!")
IndentationError: unexpected indent
>>> >>> Traceback (most recent call last):
File "/home/bswck/Python/cpython/Lib/concurrent/futures/_base.py", line 450, in result
return self.__get_result()
~~~~~~~~~~~~~~~~~^^
File "/home/bswck/Python/cpython/Lib/concurrent/futures/_base.py", line 395, in __get_result
raise self._exception
File "<stdin>", line 1, in <module>
await foo()
^^^
NameError: name 'foo' is not defined
>>>
exiting asyncio REPL...
and monkey-patch the runsource
implementation one MRO entry higher to mitigate the exception:
❯ ./python -m asyncio <<< $'import __main__, code; __main__.console.runsource = code.InteractiveConsole.runsource.__get__(__main__.console)\nasync def foo(): print("Hello!")\n\nawait foo()'
asyncio REPL 3.15.0a0 (heads/main:5f91d5d9a41, Oct 12 2025, 22:21:59) [GCC 15.2.1 20250808 (Red Hat 15.2.1-1)] on linux
Use "await" directly instead of "asyncio.run()".
Type "help", "copyright", "credits" or "license" for more information.
>>> import asyncio
>>> >>> ... >>> Hello!
>>>
exiting asyncio REPL...
I'm seeing one easy workaround here that builds on top of the existing asyncio REPL's compatibility code -- override code.InteractiveColoredConsole.runsource
to simply run code.InteractiveConsole.runsource()
on self when CAN_USE_PYREPL
is false. The entire flow of these abstractions is very weirdly tangled already, but I think that this change is surgical enough to not be a huge problem.
We could also inherit from code.InteractiveConsole
instead of code.InteractiveColoredConsole
, if CAN_USE_PYREPL
is false, either by moving where the class is defined or reassigning __bases__
, but that complicates a lot and without duplicating code loses us colored tracebacks that are the original behavior of sys._baserepl
. We likely don't want to do that.
CPython versions tested on:
CPython main branch
Operating systems tested on:
Linux
Linked PRs
Metadata
Metadata
Assignees
Labels
Projects
Status