diff --git a/django/core/management/commands/shell.py b/django/core/management/commands/shell.py index 3e8a451e3e..fe24f4b3fb 100644 --- a/django/core/management/commands/shell.py +++ b/django/core/management/commands/shell.py @@ -42,25 +42,8 @@ class Command(BaseCommand): def python(self, options): import code - # Set up a dictionary to serve as the environment for the shell, so - # that tab completion works on objects that are imported at runtime. + # Set up a dictionary to serve as the environment for the shell. imported_objects = {} - try: # Try activating rlcompleter, because it's handy. - import readline - except ImportError: - pass - else: - # We don't have to wrap the following import in a 'try', because - # we already know 'readline' was imported successfully. - import rlcompleter - readline.set_completer(rlcompleter.Completer(imported_objects).complete) - # Enable tab completion on systems using libedit (e.g. macOS). - # These lines are copied from Python's Lib/site.py. - readline_doc = getattr(readline, '__doc__', '') - if readline_doc is not None and 'libedit' in readline_doc: - readline.parse_and_bind("bind ^I rl_complete") - else: - readline.parse_and_bind("tab:complete") # We want to honor both $PYTHONSTARTUP and .pythonrc.py, so follow system # conventions and get $PYTHONSTARTUP first then .pythonrc.py. @@ -79,6 +62,27 @@ class Command(BaseCommand): except Exception: traceback.print_exc() + # By default, this will set up readline to do tab completion and to read and + # write history to the .python_history file, but this can be overridden by + # $PYTHONSTARTUP or ~/.pythonrc.py. + try: + sys.__interactivehook__() + except Exception: + # Match the behavior of the cpython shell where an error in + # sys.__interactivehook__ prints a warning and the exception and continues. + print("Failed calling sys.__interactivehook__") + traceback.print_exc() + + # Set up tab completion for objects imported by $PYTHONSTARTUP or + # ~/.pythonrc.py. + try: + import readline + import rlcompleter + readline.set_completer(rlcompleter.Completer(imported_objects).complete) + except ImportError: + pass + + # Start the interactive interpreter. code.interact(local=imported_objects) def handle(self, **options): diff --git a/docs/releases/4.0.txt b/docs/releases/4.0.txt index a9e660f0f1..9b57a524aa 100644 --- a/docs/releases/4.0.txt +++ b/docs/releases/4.0.txt @@ -236,6 +236,11 @@ Management Commands * On PostgreSQL, :djadmin:`dbshell` now supports specifying a password file. +* The :djadmin:`shell` command now respects :py:data:`sys.__interactivehook__` + at startup. This allows loading shell history between interactive sessions. + As a consequence, ``readline`` is no longer loaded if running in *isolated* + mode. + Migrations ~~~~~~~~~~