Commit 6a70cb53 authored by Tim Graham's avatar Tim Graham
Browse files

Refs #19973 -- Removed optparse support in management commands per deprecation timeline.

parent 3bbebd06
Loading
Loading
Loading
Loading
+14 −19
Original line number Diff line number Diff line
@@ -100,19 +100,16 @@ def call_command(name, *args, **options):

    # Simulate argument parsing to get the option defaults (see #10080 for details).
    parser = command.create_parser('', name)
    if command.use_argparse:
    # Use the `dest` option name from the parser option
        opt_mapping = {sorted(s_opt.option_strings)[0].lstrip('-').replace('-', '_'): s_opt.dest
                       for s_opt in parser._actions if s_opt.option_strings}
    opt_mapping = {
        sorted(s_opt.option_strings)[0].lstrip('-').replace('-', '_'): s_opt.dest
        for s_opt in parser._actions if s_opt.option_strings
    }
    arg_options = {opt_mapping.get(key, key): value for key, value in options.items()}
    defaults = parser.parse_args(args=args)
    defaults = dict(defaults._get_kwargs(), **arg_options)
    # Move positional args out of options to mimic legacy optparse
    args = defaults.pop('args', ())
    else:
        # Legacy optparse method
        defaults, _ = parser.parse_args(args=[])
        defaults = dict(defaults.__dict__, **options)
    if 'skip_checks' not in options:
        defaults['skip_checks'] = True

@@ -249,12 +246,10 @@ class ManagementUtility(object):
                    # user will find out once they execute the command.
                    pass
            parser = subcommand_cls.create_parser('', cwords[0])
            if subcommand_cls.use_argparse:
                options.extend((sorted(s_opt.option_strings)[0], s_opt.nargs != 0) for s_opt in
                               parser._actions if s_opt.option_strings)
            else:
                options.extend((s_opt.get_opt_string(), s_opt.nargs != 0) for s_opt in
                               parser.option_list)
            options.extend(
                (sorted(s_opt.option_strings)[0], s_opt.nargs != 0)
                for s_opt in parser._actions if s_opt.option_strings
            )
            # filter out previously specified options from available options
            prev_opts = [x.split('=')[0] for x in cwords[1:cword - 1]]
            options = [opt for opt in options if opt[0] not in prev_opts]
+24 −80
Original line number Diff line number Diff line
@@ -9,7 +9,6 @@ import os
import sys
import warnings
from argparse import ArgumentParser
from optparse import OptionParser

import django
from django.core import checks
@@ -152,12 +151,6 @@ class BaseCommand(object):

    Several attributes affect behavior at various steps along the way:

    ``args``
        A string listing the arguments accepted by the command,
        suitable for use in help messages; e.g., a command which takes
        a list of application names might set this to '<app_label
        app_label ...>'.

    ``can_import_settings``
        A boolean indicating whether the command needs to be able to
        import Django settings; if ``True``, ``execute()`` will verify
@@ -168,12 +161,6 @@ class BaseCommand(object):
        A short description of the command, which will be printed in
        help messages.

    ``option_list``
        This is the list of ``optparse`` options which will be fed
        into the command's ``OptionParser`` for parsing arguments.
        Deprecated and will be removed in Django 1.10.
        Use ``add_arguments`` instead.

    ``output_transaction``
        A boolean indicating whether the command outputs SQL
        statements; if ``True``, the output will automatically be
@@ -207,9 +194,7 @@ class BaseCommand(object):
        to settings. This condition will generate a CommandError.
    """
    # Metadata about this command.
    option_list = ()
    help = ''
    args = ''

    # Configuration shortcuts that alter various logic.
    _called_from_command_line = False
@@ -227,10 +212,6 @@ class BaseCommand(object):
            self.style = color_style()
            self.stderr.style_func = self.style.ERROR

    @property
    def use_argparse(self):
        return not bool(self.option_list)

    def get_version(self):
        """
        Return the Django version, which should be correct for all
@@ -255,36 +236,6 @@ class BaseCommand(object):
        Create and return the ``ArgumentParser`` which will be used to
        parse the arguments to this command.
        """
        if not self.use_argparse:
            def store_as_int(option, opt_str, value, parser):
                setattr(parser.values, option.dest, int(value))

            # Backwards compatibility: use deprecated optparse module
            warnings.warn("OptionParser usage for Django management commands "
                          "is deprecated, use ArgumentParser instead",
                          RemovedInDjango110Warning)
            parser = OptionParser(prog=prog_name,
                                usage=self.usage(subcommand),
                                version=self.get_version())
            parser.add_option('-v', '--verbosity', action='callback', dest='verbosity', default=1,
                type='choice', choices=['0', '1', '2', '3'], callback=store_as_int,
                help='Verbosity level; 0=minimal output, 1=normal output, 2=verbose output, 3=very verbose output')
            parser.add_option('--settings',
                help=(
                    'The Python path to a settings module, e.g. '
                    '"myproject.settings.main". If this isn\'t provided, the '
                    'DJANGO_SETTINGS_MODULE environment variable will be used.'
                ),
            )
            parser.add_option('--pythonpath',
                help='A directory to add to the Python path, e.g. "/home/djangoprojects/myproject".'),
            parser.add_option('--traceback', action='store_true',
                help='Raise on CommandError exceptions')
            parser.add_option('--no-color', action='store_true', dest='no_color', default=False,
                help="Don't colorize the command output.")
            for opt in self.option_list:
                parser.add_option(opt)
        else:
        parser = CommandParser(self, prog="%s %s" % (os.path.basename(prog_name), subcommand),
            description=self.help or None)
        parser.add_argument('--version', action='version', version=self.get_version())
@@ -304,9 +255,6 @@ class BaseCommand(object):
            help='Raise on CommandError exceptions')
        parser.add_argument('--no-color', action='store_true', dest='no_color', default=False,
            help="Don't colorize the command output.")
            if self.args:
                # Keep compatibility and always accept positional arguments, like optparse when args is set
                parser.add_argument('args', nargs='*')
        self.add_arguments(parser)
        return parser

@@ -335,14 +283,10 @@ class BaseCommand(object):
        self._called_from_command_line = True
        parser = self.create_parser(argv[0], argv[1])

        if self.use_argparse:
        options = parser.parse_args(argv[2:])
        cmd_options = vars(options)
        # Move positional args out of options to mimic legacy optparse
        args = cmd_options.pop('args', ())
        else:
            options, args = parser.parse_args(argv[2:])
            cmd_options = vars(options)
        handle_default_options(options)
        try:
            self.execute(*args, **cmd_options)
+0 −7
Original line number Diff line number Diff line
@@ -50,13 +50,6 @@ class Command(BaseCommand):
                 'default value is localhost:8081-8179.'),

        test_runner_class = get_runner(settings, self.test_runner)
        if hasattr(test_runner_class, 'option_list'):
            # Keeping compatibility with both optparse and argparse at this level
            # would be too heavy for a non-critical item
            raise RuntimeError(
                "The method to extend accepted command-line arguments by the "
                "test management command has changed in Django 1.8. Please "
                "create an add_arguments class method to achieve this.")

        if hasattr(test_runner_class, 'add_arguments'):
            test_runner_class.add_arguments(parser)
+0 −40
Original line number Diff line number Diff line
@@ -69,16 +69,6 @@ look like this::

                self.stdout.write('Successfully closed poll "%s"' % poll_id)

.. versionchanged:: 1.8

    Before Django 1.8, management commands were based on the :py:mod:`optparse`
    module, and positional arguments were passed in ``*args`` while optional
    arguments were passed in ``**options``. Now that management commands use
    :py:mod:`argparse` for argument parsing, all arguments are passed in
    ``**options`` by default, unless you name your positional arguments to
    ``args`` (compatibility mode). You are encouraged to exclusively use
    ``**options`` for new commands.

.. _management-commands-output:

.. note::
@@ -128,12 +118,6 @@ options can be added in the :meth:`~BaseCommand.add_arguments` method like this:
                poll.delete()
            # ...

.. versionchanged:: 1.8

    Previously, only the standard :py:mod:`optparse` library was supported and
    you would have to extend the command ``option_list`` variable with
    ``optparse.make_option()``.

The option (``delete`` in our example) is available in the options dict
parameter of the handle method. See the :py:mod:`argparse` Python documentation
for more about ``add_argument`` usage.
@@ -227,19 +211,6 @@ Attributes
All attributes can be set in your derived class and can be used in
:class:`BaseCommand`’s :ref:`subclasses<ref-basecommand-subclasses>`.

.. attribute:: BaseCommand.args

    A string listing the arguments accepted by the command,
    suitable for use in help messages; e.g., a command which takes
    a list of application names might set this to '<app_label
    app_label ...>'.

    .. deprecated:: 1.8

        This should be done now in the :meth:`~BaseCommand.add_arguments()`
        method, by calling the ``parser.add_argument()`` method. See the
        ``closepoll`` example above.

.. attribute:: BaseCommand.can_import_settings

    A boolean indicating whether the command needs to be able to
@@ -261,17 +232,6 @@ All attributes can be set in your derived class and can be used in
    the message error returned in the case of missing arguments. The default is
    output by :py:mod:`argparse` ("too few arguments").

.. attribute:: BaseCommand.option_list

    This is the list of ``optparse`` options which will be fed
    into the command's ``OptionParser`` for parsing arguments.

    .. deprecated:: 1.8

        You should now override the :meth:`~BaseCommand.add_arguments` method
        to add custom arguments accepted by your command. See :ref:`the example
        above <custom-commands-options>`.

.. attribute:: BaseCommand.output_transaction

    A boolean indicating whether the command outputs SQL statements; if
+7 −8
Original line number Diff line number Diff line
@@ -738,15 +738,14 @@ Management commands that only accept positional arguments
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

If you have written a custom management command that only accepts positional
arguments and you didn't specify the
:attr:`~django.core.management.BaseCommand.args` command variable, you might
get an error like ``Error: unrecognized arguments: ...``, as variable parsing
is now based on :py:mod:`argparse` which doesn't implicitly accept positional
arguments and you didn't specify the ``args`` command variable, you might get
an error like ``Error: unrecognized arguments: ...``, as variable parsing is
now based on :py:mod:`argparse` which doesn't implicitly accept positional
arguments. You can make your command backwards compatible by simply setting the
:attr:`~django.core.management.BaseCommand.args` class variable. However, if
you don't have to keep compatibility with older Django versions, it's better to
implement the new :meth:`~django.core.management.BaseCommand.add_arguments`
method as described in :doc:`/howto/custom-management-commands`.
``args`` class variable. However, if you don't have to keep compatibility with
older Django versions, it's better to implement the new
:meth:`~django.core.management.BaseCommand.add_arguments` method as described
in :doc:`/howto/custom-management-commands`.

Custom test management command arguments through test runner
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Loading