From b5949eac6220ee8002971b5e7026432ac7990c74 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Lapeyre?= Date: Fri, 1 Mar 2024 20:39:16 +0100 Subject: [PATCH] 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 Co-authored-by: Itamar Oren --- Lib/asyncio/__main__.py | 16 ++++ Lib/site.py | 80 ++++++++++--------- .../2020-05-29-18-08-54.bpo-40818.Ij8ffq.rst | 3 + 3 files changed, 61 insertions(+), 38 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2020-05-29-18-08-54.bpo-40818.Ij8ffq.rst diff --git a/Lib/asyncio/__main__.py b/Lib/asyncio/__main__.py index 18bb87a5bc4..cbc1d7c93ef 100644 --- a/Lib/asyncio/__main__.py +++ b/Lib/asyncio/__main__.py @@ -3,6 +3,7 @@ import asyncio import code import concurrent.futures import inspect +import site import sys import threading import types @@ -109,6 +110,21 @@ if __name__ == '__main__': 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() diff --git a/Lib/site.py b/Lib/site.py index 0631f3f6115..2aee63e24ca 100644 --- a/Lib/site.py +++ b/Lib/site.py @@ -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 diff --git a/Misc/NEWS.d/next/Library/2020-05-29-18-08-54.bpo-40818.Ij8ffq.rst b/Misc/NEWS.d/next/Library/2020-05-29-18-08-54.bpo-40818.Ij8ffq.rst new file mode 100644 index 00000000000..27f6a6daa52 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2020-05-29-18-08-54.bpo-40818.Ij8ffq.rst @@ -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.