Repository navigation
Multi-line input doesn't work in asyncio old REPL #140013
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 1We 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
- StatusShow more project fieldsDone
Activity
johnslavik commentedon Oct 12, 2025
One tricky thing about this is that there is still a small incompatibility between the new and the old REPL in terms of compilation.
Because the new REPL splits the input into statements and compiles it with
mode="exec"and then wraps particular statements withast.Interactive/ast.Moduleand re-compiles (finalizes from AST-only form) withmode="exec"/mode="single", the new REPL would accept multiple interactive statements in one prompt while the old REPL wouldn't, leading to this effect:FWIW, the regular old Python REPL struggles with the same problem:
So this isn't a flaw of this solution, just a flaw of the old REPL.
We can definitely improve the error message though, I think.
kumaraditya303 commentedon Oct 23, 2025
Since this has never worked, instead of adding this why not just recommend to use new repl instead?
johnslavik commentedon Oct 23, 2025
How would this recommendation of the new REPL be implemented?
kumaraditya303 commentedon Oct 23, 2025
We can recommend to not use PYTHON_BASIC_REPL with asyncio repl at https://docs.python.org/3.13/library/asyncio.html
johnslavik commentedon Oct 23, 2025
It is unclear exactly how many people would benefit from my original patch, so I agree on deferring it.
The basic REPL also runs as a fallback if the new REPL can't be launched, so I think it has to be more general.
How about "It is recommended to use the asyncio REPL in the Python-based REPL mode"?
kumaraditya303 commentedon Oct 24, 2025
Yes, I think that's appropriate, we can iterate over the text in the PR.
johnslavik commentedon Oct 24, 2025
Filed #140569 to track. Closing this as not planned.