-
-
Couldn't load subscription status.
- Fork 33.3k
Description
Bug report
Bug description:
Combining a custom REPL with -i is currently ramshackle, because pymain_run_python doesn't know that something (either a file, or a module, or stdin) acted as a REPL and therefore a new one right after executing the original code isn't desired:
Lines 681 to 697 in 5f91d5d
| if (config->run_command) { | |
| *exitcode = pymain_run_command(config->run_command); | |
| } | |
| else if (config->run_module) { | |
| *exitcode = pymain_run_module(config->run_module, 1); | |
| } | |
| else if (main_importer_path != NULL) { | |
| *exitcode = pymain_run_module(L"__main__", 0); | |
| } | |
| else if (config->run_filename != NULL) { | |
| *exitcode = pymain_run_file(config); | |
| } | |
| else { | |
| *exitcode = pymain_run_stdin(config); | |
| } | |
| pymain_repl(config, exitcode); |
This leads to broken expectations around python -i -m pdb or python -i -m asyncio:
Broken output of python -i -m asyncio
python -i -m asyncio❯ ./python -i -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
>>> exit
exiting asyncio REPL...
Traceback (most recent call last):
File "<frozen runpy>", line 198, in _run_module_as_main
File "<frozen runpy>", line 88, in _run_code
File "/home/bswck/Python/cpython/Lib/asyncio/__main__.py", line 240, in <module>
sys.exit(return_code)
~~~~~~~~^^^^^^^^^^^^^
SystemExit: 0
>>> exit
Broken output of python -i -m pdb
python -i -m pdb❯ ./python -i -m pdb
usage: python -m pdb [-h] [-c command] (-m module | -p pid | pyfile) [args ...]
Debug the Python program given by pyfile. Alternatively,
an executable module or package to debug can be specified using
the -m switch. You can also attach to a running Python process
using the -p option with its PID.
Initial commands are read from .pdbrc files in your home directory
and in the current directory, if they exist. Commands supplied with
-c are executed after commands from .pdbrc files.
To let the script run until an exception occurs, use "-c continue".
To let the script run up to a given line X in the debugged file, use
"-c 'until X'".
options:
-h, --help show this help message and exit
-c, --command command
pdb commands to execute as if given in a .pdbrc file
-m module
-p, --pid PID attach to the specified PID
Traceback (most recent call last):
File "<frozen runpy>", line 198, in _run_module_as_main
File "<frozen runpy>", line 88, in _run_code
File "/home/bswck/Python/cpython/Lib/pdb.py", line 3650, in <module>
pdb.main()
~~~~~~~~^^
File "/home/bswck/Python/cpython/Lib/pdb.py", line 3568, in main
sys.exit(2)
~~~~~~~~^^^
SystemExit: 2
>>> exit
Broken output of python -i -m code
python -i -m code❯ ./python -i -m code
Python 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
Type "help", "copyright", "credits" or "license" for more information.
(InteractiveConsole)
>>> exit()
now exiting InteractiveConsole...
Traceback (most recent call last):
File "<frozen runpy>", line 198, in _run_module_as_main
File "<frozen runpy>", line 88, in _run_code
File "/home/bswck/Python/cpython/Lib/code.py", line 397, in <module>
interact(banner)
~~~~~~~~^^^^^^^^
File "/home/bswck/Python/cpython/Lib/code.py", line 383, in interact
console.interact(banner, exitmsg)
~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^
File "/home/bswck/Python/cpython/Lib/code.py", line 287, in interact
raise e
File "/home/bswck/Python/cpython/Lib/code.py", line 277, in interact
more = self.push(line)
File "/home/bswck/Python/cpython/Lib/code.py", line 325, in push
more = self.runsource(source, filename, symbol=_symbol)
File "/home/bswck/Python/cpython/Lib/code.py", line 75, in runsource
self.runcode(code)
~~~~~~~~~~~~^^^^^^
File "/home/bswck/Python/cpython/Lib/code.py", line 91, in runcode
exec(code, self.locals)
~~~~^^^^^^^^^^^^^^^^^^^
File "<console>", line 1, in <module>
File "<frozen _sitebuiltins>", line 26, in __call__
SystemExit: None
warning: can't use pyrepl: I/O operation on closed file
>>> exit()
This is an easy fix. Well, we could avoid the problem by just documenting here that combining this flag isn't supported and perhaps guard against sys.flags.interactive in custom REPLs, but I dont think it's reasonable at all in this case.
We can simply introduce a flag (like, sys._running_custom_repl) or a function (like, sys._enter_custom_repl, that could raise an error if re-entered) that would inform the pymain_run_python function back that something already acted as the REPL for this runtime. The scope of that feature would be tightly coupled to pymain_run_python only! The feature could be then used in interact() or wherever it fits the most.
I don't have a clear proposal regarding the constraints that sys._enter_custom_repl would check; that's a work in progress and I'm open to suggestions.
CPython versions tested on:
CPython main branch
Operating systems tested on:
Linux