diff --git a/django/core/management/commands/shell.py b/django/core/management/commands/shell.py index d8bded0673..7d2ce62070 100644 --- a/django/core/management/commands/shell.py +++ b/django/core/management/commands/shell.py @@ -1,20 +1,23 @@ import os +import warnings from django.core.management.base import BaseCommand +from django.utils.deprecation import RemovedInDjango20Warning class Command(BaseCommand): help = "Runs a Python interactive interpreter. Tries to use IPython or bpython, if one of them is available." requires_system_checks = False - shells = ['ipython', 'bpython'] + shells = ['ipython', 'bpython', 'python'] def add_arguments(self, parser): parser.add_argument('--plain', action='store_true', dest='plain', - help='Tells Django to use plain Python, not IPython or bpython.') + help='Tells Django to use plain Python, not IPython or bpython. ' + 'Deprecated, use the `-i python` or `--interface python` option instead.') parser.add_argument('--no-startup', action='store_true', dest='no_startup', help='When using plain Python, ignore the PYTHONSTARTUP environment variable and ~/.pythonrc.py script.') parser.add_argument('-i', '--interface', choices=self.shells, dest='interface', - help='Specify an interactive interpreter interface. Available options: "ipython" and "bpython"') + help='Specify an interactive interpreter interface. Available options: "ipython", "bpython", and "python"') def _ipython_pre_011(self): """Start IPython pre-0.11""" @@ -34,7 +37,7 @@ class Command(BaseCommand): from IPython import start_ipython start_ipython(argv=[]) - def ipython(self): + def ipython(self, options): """Start any version of IPython""" for ip in (self._ipython, self._ipython_pre_100, self._ipython_pre_011): try: @@ -46,56 +49,55 @@ class Command(BaseCommand): # no IPython, raise ImportError raise ImportError("No IPython") - def bpython(self): + def bpython(self, options): import bpython bpython.embed() - def run_shell(self, shell=None): - available_shells = [shell] if shell else self.shells + 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. + 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) + 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. + if not options['no_startup']: + for pythonrc in (os.environ.get("PYTHONSTARTUP"), '~/.pythonrc.py'): + if not pythonrc: + continue + pythonrc = os.path.expanduser(pythonrc) + if not os.path.isfile(pythonrc): + continue + try: + with open(pythonrc) as handle: + exec(compile(handle.read(), pythonrc, 'exec'), imported_objects) + except NameError: + pass + code.interact(local=imported_objects) + + def handle(self, **options): + if options['plain']: + warnings.warn( + "The --plain option is deprecated in favor of the -i python or --interface python option.", + RemovedInDjango20Warning + ) + options['interface'] = 'python' + + available_shells = [options['interface']] if options['interface'] else self.shells for shell in available_shells: try: - return getattr(self, shell)() + return getattr(self, shell)(options) except ImportError: pass - raise ImportError - - def handle(self, **options): - try: - if options['plain']: - # Don't bother loading IPython, because the user wants plain Python. - raise ImportError - - self.run_shell(shell=options['interface']) - except ImportError: - 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. - # See ticket 5082. - 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) - 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. - if not options['no_startup']: - for pythonrc in (os.environ.get("PYTHONSTARTUP"), '~/.pythonrc.py'): - if not pythonrc: - continue - pythonrc = os.path.expanduser(pythonrc) - if not os.path.isfile(pythonrc): - continue - try: - with open(pythonrc) as handle: - exec(compile(handle.read(), pythonrc, 'exec'), imported_objects) - except NameError: - pass - code.interact(local=imported_objects) + raise ImportError("Couldn't load any of the specified interfaces.") diff --git a/docs/internals/deprecation.txt b/docs/internals/deprecation.txt index 5fe2e142fb..d0bafdaafe 100644 --- a/docs/internals/deprecation.txt +++ b/docs/internals/deprecation.txt @@ -126,6 +126,8 @@ details on these changes. * ``django.utils.functional.allow_lazy()`` will be removed. +* The ``shell --plain`` option will be removed. + .. _deprecation-removed-in-1.10: 1.10 diff --git a/docs/ref/django-admin.txt b/docs/ref/django-admin.txt index d7798e559a..31da49cfc5 100644 --- a/docs/ref/django-admin.txt +++ b/docs/ref/django-admin.txt @@ -933,9 +933,15 @@ Starts the Python interactive interpreter. Django will use IPython_ or bpython_ if either is installed. If you have a rich shell installed but want to force use of the "plain" Python interpreter, -use the ``--plain`` option, like so:: +use the ``-i python`` or ``--interface python`` option, like so:: - django-admin shell --plain + django-admin shell -i python + django-admin shell --interface python + +.. deprecated:: 1.10 + + In older versions, use the ``--plain`` option. This is deprecated and will + be removed in Django 2.0. If you would like to specify either IPython or bpython as your interpreter if you have both installed you can specify an alternative interpreter interface @@ -957,12 +963,12 @@ bpython:: .. _bpython: http://bpython-interpreter.org/ When the "plain" Python interactive interpreter starts (be it because -``--plain`` was specified or because no other interactive interface is -available) it reads the script pointed to by the :envvar:`PYTHONSTARTUP` +``--interface python`` was specified or because no other interactive interface +is available) it reads the script pointed to by the :envvar:`PYTHONSTARTUP` environment variable and the ``~/.pythonrc.py`` script. If you don't wish this behavior you can use the ``--no-startup`` option. e.g.:: - django-admin shell --plain --no-startup + django-admin shell --interface python --no-startup showmigrations [ []] ------------------------------------------ diff --git a/docs/releases/1.10.txt b/docs/releases/1.10.txt index 7b360948a7..6276f2e102 100644 --- a/docs/releases/1.10.txt +++ b/docs/releases/1.10.txt @@ -212,6 +212,9 @@ Management Commands * :djadmin:`makemigrations` now displays the path to the migration files that it generates. +* The :djadmin:`shell` ``--interface`` option now accepts ``python`` to force + use of the "plain" Python interpreter. + Migrations ^^^^^^^^^^ @@ -432,6 +435,9 @@ Miscellaneous :func:`~django.utils.functional.keep_lazy` function which can be used with a more natural decorator syntax. +* The ``shell --plain`` option is deprecated in favor of ``-i python`` or + ``--interface python``. + .. _removed-features-1.10: Features removed in 1.10