Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 17 additions & 4 deletions Doc/library/code.rst
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ build applications which provide an interactive interpreter prompt.
.. versionchanged:: 3.13
Added *local_exit* parameter.

.. function:: interact(banner=None, readfunc=None, local=None, exitmsg=None, local_exit=False)
.. function:: interact(banner=None, readfunc=None, local=None, exitmsg=None, local_exit=False, *, use_pyrepl=False)

Convenience function to run a read-eval-print loop. This creates a new
instance of :class:`InteractiveConsole` and sets *readfunc* to be used as
Expand All @@ -49,15 +49,19 @@ build applications which provide an interactive interpreter prompt.
use as the default namespace for the interpreter loop. If *local_exit* is provided,
it is passed to the :class:`InteractiveConsole` constructor. The :meth:`~InteractiveConsole.interact`
method of the instance is then run with *banner* and *exitmsg* passed as the
banner and exit message to use, if provided. The console object is discarded
after use.
banner and exit message to use, if provided. The *use_pyrepl* argument is
passed to :meth:`~InteractiveConsole.interact` to control whether to use the
pyrepl-based REPL. The console object is discarded after use.

.. versionchanged:: 3.6
Added *exitmsg* parameter.

.. versionchanged:: 3.13
Added *local_exit* parameter.

.. versionchanged:: next
Added *use_pyrepl* parameter.

.. function:: compile_command(source, filename="<input>", symbol="single")

This function is useful for programs that want to emulate Python's interpreter
Expand Down Expand Up @@ -153,7 +157,7 @@ The :class:`InteractiveConsole` class is a subclass of
interpreter objects as well as the following additions.


.. method:: InteractiveConsole.interact(banner=None, exitmsg=None)
.. method:: InteractiveConsole.interact(banner=None, exitmsg=None, *, use_pyrepl=False)

Closely emulate the interactive Python console. The optional *banner* argument
specify the banner to print before the first interaction; by default it prints a
Expand All @@ -165,12 +169,21 @@ interpreter objects as well as the following additions.
Pass the empty string to suppress the exit message. If *exitmsg* is not given or
``None``, a default message is printed.

The optional *use_pyrepl* argument controls whether to use the pyrepl-based REPL
when available. When ``True``, pyrepl is used. When ``False`` (the default),
the basic readline-based REPL is used. When ``None``, pyrepl is used
automatically if available and the :envvar:`PYTHON_BASIC_REPL` environment
variable is not set.
Comment on lines +174 to +176
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not make None behavior - default, and get rid of the use_pyrepl option? Users can override this by setting PYTHON_BASIC_REPL.

I presume, new option was chosen on ground backward-compatibility, but new REPL is an incompatible change by itself, this is just a continuation.

So, I think it's fine to remove added option, but please wait first feedback from core developers.

Copy link
Copy Markdown
Contributor Author

@tanloong tanloong Apr 9, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for your review!

I believe adding the use_pyrepl parameter is necessary, and it should default to False rather than None.

Many third-party projects subclass code.InteractiveConsole to implement custom shells (1.2k search results on GitHub). CPython's own sqlite3 CLI also builds on code.InteractiveConsole and relies on GNU Readline for completion. If we enable the new REPL by default, its completion will break, and we will also see incorrect syntax highlighting: tokens in SQLite statements that coincide with Python keywords will be highlighted mistakenly by PyREPL. Similar issues would affect those among the 1.2k subclasses that implemented their own custom syntax and completions.

For backward compatibility, I think keeping use_pyrepl=False as the default is the safest approach.


.. versionchanged:: 3.4
To suppress printing any banner, pass an empty string.

.. versionchanged:: 3.6
Print an exit message when exiting.

.. versionchanged:: next
Added *use_pyrepl* parameter.


.. method:: InteractiveConsole.push(line)

Expand Down
12 changes: 12 additions & 0 deletions Doc/whatsnew/3.15.rst
Original file line number Diff line number Diff line change
Expand Up @@ -722,6 +722,18 @@ calendar
(Contributed by Pål Grønås Drange in :gh:`140212`.)


code
----

* The :meth:`code.InteractiveConsole.interact` method and the :func:`code.interact`
function now support a *use_pyrepl* parameter to control whether to use the
pyrepl-based REPL. By default, *use_pyrepl* is ``False``, using the basic
readline-based REPL. When set to ``True``, pyrepl is used if available.
When set to ``None``, pyrepl is used automatically if available and the
:envvar:`PYTHON_BASIC_REPL` environment variable is not set.
(Contributed by Long Tan in :gh:`148261`.)


collections
-----------

Expand Down
30 changes: 27 additions & 3 deletions Lib/code.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@


import builtins
import os
import sys
import traceback
from codeop import CommandCompiler, compile_command
Expand Down Expand Up @@ -201,7 +202,7 @@ def resetbuffer(self):
"""Reset the input buffer."""
self.buffer = []

def interact(self, banner=None, exitmsg=None):
def interact(self, banner=None, exitmsg=None, *, use_pyrepl=False):
"""Closely emulate the interactive Python console.

The optional banner argument specifies the banner to print
Expand All @@ -216,7 +217,29 @@ def interact(self, banner=None, exitmsg=None):
printing an exit message. If exitmsg is not given or None,
a default message is printed.

The use_pyrepl argument controls whether to use the pyrepl-based REPL
when available. When True, pyrepl is used. When False (the default),
the basic readline-based REPL is used. When None, pyrepl is used
automatically if available and the PYTHON_BASIC_REPL environment
variable is not set.

"""
if use_pyrepl is None:
use_pyrepl = not os.getenv('PYTHON_BASIC_REPL')

if use_pyrepl:
try:
from _pyrepl.main import CAN_USE_PYREPL
if CAN_USE_PYREPL:
from _pyrepl.simple_interact import (
run_multiline_interactive_console,
)
run_multiline_interactive_console(self)
return
except ImportError:
pass
# Fall through to basic REPL if pyrepl is unavailable

try:
sys.ps1
delete_ps1_after = False
Expand Down Expand Up @@ -355,7 +378,7 @@ def __call__(self, code=None):
raise SystemExit(code)


def interact(banner=None, readfunc=None, local=None, exitmsg=None, local_exit=False):
def interact(banner=None, readfunc=None, local=None, exitmsg=None, local_exit=False, *, use_pyrepl=False):
"""Closely emulate the interactive Python interpreter.

This is a backwards compatible interface to the InteractiveConsole
Expand All @@ -369,6 +392,7 @@ def interact(banner=None, readfunc=None, local=None, exitmsg=None, local_exit=Fa
local -- passed to InteractiveInterpreter.__init__()
exitmsg -- passed to InteractiveConsole.interact()
local_exit -- passed to InteractiveConsole.__init__()
use_pyrepl -- passed to InteractiveConsole.interact()

"""
console = InteractiveConsole(local, local_exit=local_exit)
Expand All @@ -379,7 +403,7 @@ def interact(banner=None, readfunc=None, local=None, exitmsg=None, local_exit=Fa
import readline # noqa: F401
except ImportError:
pass
console.interact(banner, exitmsg)
console.interact(banner, exitmsg, use_pyrepl=use_pyrepl)


if __name__ == "__main__":
Expand Down
Loading
Loading