PEP: 762
Title: REPL-acing the default REPL
Author: Pablo Galindo Salgado <pablogsal@python.org>, Łukasz Langa <lukasz@python.org>, Lysandros Nikolaou <lisandrosnik@gmail.com>, Emily Morehouse-Valcarcel <emily@python.org>
Sponsor: Pablo Galindo Salgado 
Status: Final
Type: Informational
Created: 11-Oct-2024
Python-Version: 3.13

Abstract
========

One of Python’s core strengths is its interactive mode, also known as the
Read-Eval-Print Loop (REPL), or the Python console, or the Python shell. This
PEP describes the new implementation of this functionality written in Python.
The new REPL released in Python 3.13 aims to provide modern features expected by
today's users, such as multi-line editing, syntax highlighting, custom commands,
and an overall improved interactive experience.

Motivation
==========

Up to Python 3.12, the interactive shell of CPython was written in C as a
special mode of the parser. It was therefore difficult to maintain and extend.
It relied on the existence of GNU readline (or an equivalent) for basic
functionality like cursor movement and history tracking. Python compiled without
this library provided an interactive mode of very limited capability. On the
other hand, Python compiled with readline outsourced decisions and configuration
around user input in ways that made extending it difficult.

This complexity has deterred contributions and has made it challenging to
implement new features. As a result, the CPython interactive shell has seen
minimal changes, falling behind user expectations for modern equivalents.

Many features that users have come to expect from modern REPLs were absent in
the previous version. Some examples of these features include multi-line editing
and history, custom commands, syntax highlighting, or ergonomic handling of copy
and paste. The lack of these features greatly impacts the user experience of
many groups of users of CPython, in particular in environments where users don’t
control dependencies and cannot install their own packages. This is especially
common for users learning the language and educators.

Addressing such issues with the C implementation would require complex
workarounds, such as AST matching of commands, which would add prohibitive
complexity to the codebase.

With the new REPL written in Python, we are addressing these limitations while
also bringing CPython's interactive experience more in line with modern
expectations and capabilities.

Rationale
=========

Implementing the new REPL in Python, rather than C, has significantly lowered
the barrier to entry for contributors. This change has made it easier to test,
validate, and modify the REPL, leading to increased community involvement and
faster feature development. The improved accessibility of the codebase is
expected to result in a more rapidly evolving and user-responsive REPL.

Instead of writing a Python REPL from scratch, we decided to base the
implementation of the new REPL on `PyREPL <https://github.com/pypy/pypy/tree/d102094b863ce49b7af030dcb0cecaac515d97c6/lib_pypy/pyrepl>`_.
This decision was driven by several key factors. First and foremost,
developing a terminal application that works consistently across different
operating systems and terminal emulators is a complex undertaking.
By adopting PyREPL, which has been battle-tested in the PyPy project,
we can leverage existing, proven code rather than starting from scratch.

Sharing a codebase with PyPy for the REPL implementation offers mutual benefits
to both projects. It allows for shared maintenance efforts, faster bug fixes,
and feature improvements that can benefit users of both CPython and PyPy. This
collaboration can lead to a more robust and feature-rich REPL for the entire
Python ecosystem.

The previous REPL written in C leveraged the “readline” or “editline” libraries
as a backend to allow certain features such as navigation, history preservation
and recall, autocompletion, and configurable keyboard behavior. PyREPL does not
use those libraries, implementing most of the other functionality directly as
part of the shell. The main missing functionality (configurability of input) is
outweighed by the benefits of the new architecture. The configuration files for
these libraries (e.g. inputrc) are complex and include features that PyREPL
doesn’t plan to implement, making it infeasible to transparently add support for
them in the new shell. Using “readline” or “editline” in PyREPL would be
prohibitively complex due to multi-line editing handling and multiplatform
support.

Although this means that existing readline/editline configurations will not be
compatible with the PyREPL, we believe the enhanced features and improved
extensibility are an overall win. See “Backwards Compatibility” for discussion
of continued support for customized workflows.

The previous REPL made it challenging to properly implement custom commands,
which is a very common feature of interactive shells. For instance, the ``exit``
command was implemented as a method call of a custom object injected in the
global namespace, leading to unintuitive behavior that often confuses users when
they simply type ``exit``, as the interpreter prompts them to the supposedly
correct usage ``exit()``.

Specification
=============

PyREPL is implemented as a new private Python module called :mod:`!_pyrepl`, existing
alongside the current C implementation. In its first implementation, it
introduces the following key features:

1. Multi-line History and Editing: Users are able to navigate and edit their
   command history across multiple lines, improving the ability to refine and reuse
   complex blocks of code.

   Editing multi-line blocks provides automatic indentation using four spaces, which
   is consistent with :pep:`8` recommendations. When a line ending with a colon is
   encountered, the following line is automatically indented utilizing the
   indentation pattern that is inferred from the first line that contains
   indentation. Lines are indented with four spaces, and tabs are converted into
   spaces.

   Users can access history of commands by using :kbd:`up` and :kbd:`down` arrows. Within
   a multi-line entry, the arrow keys navigate line-by-line within the block before
   moving to the next history entry. The :kbd:`down` arrow works in reverse, navigating
   from older entries to the most recent.

   History can be searched forward (using :kbd:`Ctrl+S`) and in reverse (using :kbd:`Ctrl+R`)
   using a custom-specified substring query. It can also be searched with a prefix
   query by entering the prefix into a shell line and using PageUp and PageDown
   keys.

2. Copying and Pasting: in supported terminal emulators, bracketed pasting
   capability is discovered and used by PyREPL. This allows for transparent pasting
   of blocks of code without immediate execution or invalid automatic indentation.
   
   For terminal emulators that don’t support this mode, a dedicated paste mode is
   implemented to allow for easy insertion of multi-line code snippets without
   triggering immediate execution or indentation issues.
   
   Users enter manual paste mode by hitting the :kbd:`F3` key. The prompt changes from
   ``>>>`` to ``(paste)`` where users can paste contents from their clipboard or
   manually type as desired. Once the content is ready, hitting :kbd:`F3` exits paste
   mode. Then, pressing Enter executes the block.
   
   Users can enter multiple commands on a single input when using paste mode, which
   will help paste code from other sources.
   
   To copy blocks of code without the leading command prompts and without the
   output of the commands, users can enter the history view via the :kbd:`F2` key. This
   mode uses a pager to display history of executed commands without the prompts
   and output.

3. Help via :kbd:`F1`.

   Access to the standard Help module is accessible via a Custom Command ``help``
   (see below) or via the :kbd:`F1` key. Hit :kbd:`F1` to enter help mode. When you're done, hit
   :kbd:`F1` or a standard command (``q``, ``quit`` or ``exit``) to exit.
   
   Browsing interactive help does not retain command history.

4. Custom Commands: The REPL supports the implementation of custom commands,
   such as ``exit``, in a more natural and user-friendly manner, avoiding the current
   function call workaround.
   
   The initial list of custom commands includes:

   * ``exit``
   * ``quit``
   * ``copyright``
   * ``help``
   * ``clear``
   
   Commands are available as long as there is no name conflict with a variable in a
   reachable scope. For example, after assigning ``exit = 1``, the variable will
   take precedence over PyREPL commands. ``del exit`` in this case will remove the
   conflict and the command will function again.

5. Colors: the prompts as well as certain elements of the output, like exception
   tracebacks, are now colored. Colors can be disabled using the standard
   ``NO_COLOR`` environment variable, or forced by using the standard
   ``FORCE_COLOR`` environment variable. A Python-specific environment variable is
   also available called ``PYTHON_COLORS``. The initial implementation in Python
   3.13 does not offer customization of the color theme.

These features are significantly enhancing the interactive Python experience,
bringing it more in line with modern development environments and user
expectations. The implementation is in Python, offering several advantages:

1. Easier Testing and Validation: Writing tests for Python code is dramatically
   simpler and more straightforward than for C code, allowing for more
   comprehensive test coverage of all existing and old features.

2. Lower Contribution Barrier: Python's accessibility compared to C has been
   encouraging more community contributions, leading to faster feature development
   and bug fixes.

3. Flexibility: A Python implementation is easier to extend and modify,
   improving developer velocity on new features and improvements by core developers
   and contributors alike.

Backwards Compatibility
=======================

The PyREPL implementation is designed to maintain full backward compatibility
with existing Python code as the old basic REPL will be preserved as a fallback
and is available on demand, in case custom workflows require it. It will also be
used in cases where the new REPL cannot be used due to environmental constraints
or other issues.  Users have the option to explicitly choose the old basic REPL
by setting the environment variable ``PYTHON_BASIC_REPL`` to 1. This ensures
that users can continue using the familiar interface and capabilities if they
prefer, or if they encounter any issues with the new implementation.

It's important to emphasize that the introduction of PyREPL does not remove any
existing functionality. Any functionality of the old basic REPL unavailable in
PyREPL is preserved and maintained in the old basic REPL that can be used by
users as a fallback.

In particular, users wanting to continue using their custom input configuration
in ``inputrc`` or ``editrc`` files can continue using the old basic REPL.

The authors do not expect any PyREPL functionality to be ported to the old basic
REPL. Similarly, ``inputrc`` and ``editrc`` support is explicitly not planned in
PyREPL. Those configuration files are provided by and parsed by “readline” and
“editline” libraries, and their functional scope does not match the
functionality PyREPL is targeting.

To facilitate a smooth transition, `clear documentation <https://docs.python.org/3.13/tutorial/appendix.html#interactive-mode>`_
is provided on how to switch between PyREPL and the old basic REPL.

This approach ensures that while we're introducing significant improvements with
the new REPL, we're not forcing any immediate changes on users who rely on the
current implementation. The fallback mechanism and user choice option provide a
safety net that allows for gradual adoption of the new REPL while maintaining
all existing functionality.

Security Implications
=====================

There are no security implications derived from this proposal.

How to Teach This
=================

The introduction of PyREPL is accompanied by documentation and tutorials. Key
areas of focus for education will include:

1. Detailed explanations on using multi-line editing, paste mode, and other new
   features.

2. Custom commands (existing and new).

3. How to switch to the new REPL, including any
   differences from the previous readline/editline-based configuration.

Rejected Ideas
==============

Several alternative approaches were considered and ultimately rejected:

1. Extending the current C implementation: While this would maintain maximum
   backwards compatibility, it was deemed too complex and would not address the
   fundamental limitations described ut supra.

2. Developing a new REPL from scratch: This approach was rejected due to the
   complexity of creating a cross-platform terminal application and the desire to
   leverage existing, proven code.

3. Using other existing REPL implementations: The authors looked at several
   alternatives like `IPython <https://ipython.org/>`_,
   `bpython <https://bpython-interpreter.org/>`_,
   `ptpython <https://github.com/prompt-toolkit/ptpython>`_, and
   `xonsh <https://xon.sh/>`_.  While all the above are impressive projects,
   in the end PyREPL was chosen for its combination of maturity, feature set,
   and lack of additional dependencies.  Another key factor was the alignment
   with PyPy's implementation.

   
Acknowledgments
===============

Thanks to Diego Russo for providing feedback on drafts of this PEP.

Copyright
=========

This document is placed in the public domain or under the CC0-1.0-Universal
license, whichever is more permissive.
