Last active
May 22, 2024 20:00
-
-
Save treyhunner/92265eb3ff94f3909ef2fbc761c56fd3 to your computer and use it in GitHub Desktop.
New Python REPL (Python 3.13+) in Django shell
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# fork of https://github.com/django/django/blob/b7c7209c67f742eda8184c46f139e0e1cb16a1f4/django/core/management/commands/shell.py | |
import os | |
import select | |
import sys | |
import traceback | |
from django.core.management import BaseCommand, CommandError | |
from django.utils.datastructures import OrderedSet | |
class Command(BaseCommand): | |
help = ( | |
"Runs a Python interactive interpreter. Tries to use IPython or " | |
"bpython, if one of them is available. Any standard input is executed " | |
"as code." | |
) | |
requires_system_checks = [] | |
shells = ["ipython", "bpython", "pyrepl", "python"] | |
def add_arguments(self, parser): | |
parser.add_argument( | |
"--no-startup", | |
action="store_true", | |
help=( | |
"When using plain Python, ignore the PYTHONSTARTUP environment " | |
"variable and ~/.pythonrc.py script." | |
), | |
) | |
parser.add_argument( | |
"-i", | |
"--interface", | |
choices=self.shells, | |
help=( | |
"Specify an interactive interpreter interface. Available options: " | |
'"ipython", "bpython", "pyrepl", and "python"' | |
), | |
) | |
parser.add_argument( | |
"-c", | |
"--command", | |
help=( | |
"Instead of opening an interactive shell, run a command as Django and " | |
"exit." | |
), | |
) | |
def ipython(self, options): | |
from IPython import start_ipython | |
start_ipython(argv=[]) | |
def bpython(self, options): | |
import bpython | |
bpython.embed() | |
def pyrepl(self, options): | |
from _pyrepl.simple_interact import run_multiline_interactive_console | |
run_multiline_interactive_console() | |
def python(self, options): | |
import code | |
# Set up a dictionary to serve as the environment for the shell. | |
imported_objects = {} | |
# We want to honor both $PYTHONSTARTUP and .pythonrc.py, so follow system | |
# conventions and get $PYTHONSTARTUP first then .pythonrc.py. | |
if not options["no_startup"]: | |
for pythonrc in OrderedSet( | |
[os.environ.get("PYTHONSTARTUP"), os.path.expanduser("~/.pythonrc.py")] | |
): | |
if not pythonrc: | |
continue | |
if not os.path.isfile(pythonrc): | |
continue | |
with open(pythonrc) as handle: | |
pythonrc_code = handle.read() | |
# Match the behavior of the cpython shell where an error in | |
# PYTHONSTARTUP prints an exception and continues. | |
try: | |
exec(compile(pythonrc_code, pythonrc, "exec"), imported_objects) | |
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: | |
hook = sys.__interactivehook__ | |
except AttributeError: | |
# Match the behavior of the cpython shell where a missing | |
# sys.__interactivehook__ is ignored. | |
pass | |
else: | |
try: | |
hook() | |
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): | |
# Execute the command and exit. | |
if options["command"]: | |
exec(options["command"], globals()) | |
return | |
# Execute stdin if it has anything to read and exit. | |
# Not supported on Windows due to select.select() limitations. | |
if ( | |
sys.platform != "win32" | |
and not sys.stdin.isatty() | |
and select.select([sys.stdin], [], [], 0)[0] | |
): | |
exec(sys.stdin.read(), globals()) | |
return | |
available_shells = ( | |
[options["interface"]] if options["interface"] else self.shells | |
) | |
for shell in available_shells: | |
try: | |
return getattr(self, shell)(options) | |
except ImportError: | |
pass | |
raise CommandError("Couldn't import {} interface.".format(shell)) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment