gh-84995: Run sys.__interactivehook__() on asyncio REPL startup (#20517)

This makes the asyncio REPL (`python -m asyncio`) more usable
and similar to the regular REPL.

This exposes register_readline() as a top-level function in site.py,
but it's intentionally undocumented. 

Co-authored-by: Carol Willing <carolcode@willingconsulting.com>
Co-authored-by: Itamar Oren <itamarost@gmail.com>
This commit is contained in:
Rémi Lapeyre 2024-03-01 20:39:16 +01:00 committed by GitHub
parent e6e35327d8
commit b5949eac62
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 61 additions and 38 deletions

View file

@ -3,6 +3,7 @@
import code
import concurrent.futures
import inspect
import site
import sys
import threading
import types
@ -109,6 +110,21 @@ def run(self):
except ImportError:
pass
interactive_hook = getattr(sys, "__interactivehook__", None)
if interactive_hook is not None:
interactive_hook()
if interactive_hook is site.register_readline:
# Fix the completer function to use the interactive console locals
try:
import rlcompleter
except:
pass
else:
completer = rlcompleter.Completer(console.locals)
readline.set_completer(completer.complete)
repl_thread = REPLThread()
repl_thread.daemon = True
repl_thread.start()

View file

@ -460,60 +460,64 @@ def gethistoryfile():
def enablerlcompleter():
"""Enable default readline configuration on interactive prompts, by
registering a sys.__interactivehook__.
"""
sys.__interactivehook__ = register_readline
def register_readline():
"""Configure readline completion on interactive prompts.
If the readline module can be imported, the hook will set the Tab key
as completion key and register ~/.python_history as history file.
This can be overridden in the sitecustomize or usercustomize module,
or in a PYTHONSTARTUP file.
"""
def register_readline():
import atexit
try:
import readline
import rlcompleter
except ImportError:
return
import atexit
try:
import readline
import rlcompleter
except ImportError:
return
# Reading the initialization (config) file may not be enough to set a
# completion key, so we set one first and then read the file.
if readline.backend == 'editline':
readline.parse_and_bind('bind ^I rl_complete')
else:
readline.parse_and_bind('tab: complete')
# Reading the initialization (config) file may not be enough to set a
# completion key, so we set one first and then read the file.
if readline.backend == 'editline':
readline.parse_and_bind('bind ^I rl_complete')
else:
readline.parse_and_bind('tab: complete')
try:
readline.read_init_file()
except OSError:
# An OSError here could have many causes, but the most likely one
# is that there's no .inputrc file (or .editrc file in the case of
# Mac OS X + libedit) in the expected location. In that case, we
# want to ignore the exception.
pass
if readline.get_current_history_length() == 0:
# If no history was loaded, default to .python_history,
# or PYTHON_HISTORY.
# The guard is necessary to avoid doubling history size at
# each interpreter exit when readline was already configured
# through a PYTHONSTARTUP hook, see:
# http://bugs.python.org/issue5845#msg198636
history = gethistoryfile()
try:
readline.read_init_file()
readline.read_history_file(history)
except OSError:
# An OSError here could have many causes, but the most likely one
# is that there's no .inputrc file (or .editrc file in the case of
# Mac OS X + libedit) in the expected location. In that case, we
# want to ignore the exception.
pass
if readline.get_current_history_length() == 0:
# If no history was loaded, default to .python_history,
# or PYTHON_HISTORY.
# The guard is necessary to avoid doubling history size at
# each interpreter exit when readline was already configured
# through a PYTHONSTARTUP hook, see:
# http://bugs.python.org/issue5845#msg198636
history = gethistoryfile()
def write_history():
try:
readline.read_history_file(history)
except OSError:
readline.write_history_file(history)
except (FileNotFoundError, PermissionError):
# home directory does not exist or is not writable
# https://bugs.python.org/issue19891
pass
def write_history():
try:
readline.write_history_file(history)
except OSError:
# bpo-19891, bpo-41193: Home directory does not exist
# or is not writable, or the filesystem is read-only.
pass
atexit.register(write_history)
atexit.register(write_history)
sys.__interactivehook__ = register_readline
def venv(known_paths):
global PREFIXES, ENABLE_USER_SITE

View file

@ -0,0 +1,3 @@
The asyncio REPL now runs :data:`sys.__interactivehook__` on startup. The
default implementation of :data:`sys.__interactivehook__` provides
auto-completion to the asyncio REPL. Patch contributed by Rémi Lapeyre.