Skip to content

Commit b5949ea

Browse files
Rémi Lapeyrewillingcitamaro
authored
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>
1 parent e6e3532 commit b5949ea

File tree

3 files changed

+62
-39
lines changed

3 files changed

+62
-39
lines changed

Lib/asyncio/__main__.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import code
44
import concurrent.futures
55
import inspect
6+
import site
67
import sys
78
import threading
89
import types
@@ -109,6 +110,21 @@ def run(self):
109110
except ImportError:
110111
pass
111112

113+
interactive_hook = getattr(sys, "__interactivehook__", None)
114+
115+
if interactive_hook is not None:
116+
interactive_hook()
117+
118+
if interactive_hook is site.register_readline:
119+
# Fix the completer function to use the interactive console locals
120+
try:
121+
import rlcompleter
122+
except:
123+
pass
124+
else:
125+
completer = rlcompleter.Completer(console.locals)
126+
readline.set_completer(completer.complete)
127+
112128
repl_thread = REPLThread()
113129
repl_thread.daemon = True
114130
repl_thread.start()

Lib/site.py

Lines changed: 43 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -460,60 +460,64 @@ def gethistoryfile():
460460
def enablerlcompleter():
461461
"""Enable default readline configuration on interactive prompts, by
462462
registering a sys.__interactivehook__.
463+
"""
464+
sys.__interactivehook__ = register_readline
465+
466+
467+
def register_readline():
468+
"""Configure readline completion on interactive prompts.
463469
464470
If the readline module can be imported, the hook will set the Tab key
465471
as completion key and register ~/.python_history as history file.
466472
This can be overridden in the sitecustomize or usercustomize module,
467473
or in a PYTHONSTARTUP file.
468474
"""
469-
def register_readline():
470-
import atexit
471-
try:
472-
import readline
473-
import rlcompleter
474-
except ImportError:
475-
return
476-
477-
# Reading the initialization (config) file may not be enough to set a
478-
# completion key, so we set one first and then read the file.
479-
if readline.backend == 'editline':
480-
readline.parse_and_bind('bind ^I rl_complete')
481-
else:
482-
readline.parse_and_bind('tab: complete')
475+
import atexit
476+
try:
477+
import readline
478+
import rlcompleter
479+
except ImportError:
480+
return
483481

482+
# Reading the initialization (config) file may not be enough to set a
483+
# completion key, so we set one first and then read the file.
484+
if readline.backend == 'editline':
485+
readline.parse_and_bind('bind ^I rl_complete')
486+
else:
487+
readline.parse_and_bind('tab: complete')
488+
489+
try:
490+
readline.read_init_file()
491+
except OSError:
492+
# An OSError here could have many causes, but the most likely one
493+
# is that there's no .inputrc file (or .editrc file in the case of
494+
# Mac OS X + libedit) in the expected location. In that case, we
495+
# want to ignore the exception.
496+
pass
497+
498+
if readline.get_current_history_length() == 0:
499+
# If no history was loaded, default to .python_history,
500+
# or PYTHON_HISTORY.
501+
# The guard is necessary to avoid doubling history size at
502+
# each interpreter exit when readline was already configured
503+
# through a PYTHONSTARTUP hook, see:
504+
# http://bugs.python.org/issue5845#msg198636
505+
history = gethistoryfile()
484506
try:
485-
readline.read_init_file()
507+
readline.read_history_file(history)
486508
except OSError:
487-
# An OSError here could have many causes, but the most likely one
488-
# is that there's no .inputrc file (or .editrc file in the case of
489-
# Mac OS X + libedit) in the expected location. In that case, we
490-
# want to ignore the exception.
491509
pass
492510

493-
if readline.get_current_history_length() == 0:
494-
# If no history was loaded, default to .python_history,
495-
# or PYTHON_HISTORY.
496-
# The guard is necessary to avoid doubling history size at
497-
# each interpreter exit when readline was already configured
498-
# through a PYTHONSTARTUP hook, see:
499-
# http://bugs.python.org/issue5845#msg198636
500-
history = gethistoryfile()
511+
def write_history():
501512
try:
502-
readline.read_history_file(history)
503-
except OSError:
513+
readline.write_history_file(history)
514+
except (FileNotFoundError, PermissionError):
515+
# home directory does not exist or is not writable
516+
# https://bugs.python.org/issue19891
504517
pass
505518

506-
def write_history():
507-
try:
508-
readline.write_history_file(history)
509-
except OSError:
510-
# bpo-19891, bpo-41193: Home directory does not exist
511-
# or is not writable, or the filesystem is read-only.
512-
pass
519+
atexit.register(write_history)
513520

514-
atexit.register(write_history)
515-
516-
sys.__interactivehook__ = register_readline
517521

518522
def venv(known_paths):
519523
global PREFIXES, ENABLE_USER_SITE
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
The asyncio REPL now runs :data:`sys.__interactivehook__` on startup. The
2+
default implementation of :data:`sys.__interactivehook__` provides
3+
auto-completion to the asyncio REPL. Patch contributed by Rémi Lapeyre.

0 commit comments

Comments
 (0)