Browse Source

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

Tim Graham 9 years ago
parent
commit
6a70cb5397

+ 14 - 19
django/core/management/__init__.py

@@ -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}
-        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)
+    # 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
+    }
+    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', ())
     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
django/core/management/base.py

@@ -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,59 +236,26 @@ 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())
-            parser.add_argument('-v', '--verbosity', action='store', dest='verbosity', default='1',
-                type=int, choices=[0, 1, 2, 3],
-                help='Verbosity level; 0=minimal output, 1=normal output, 2=verbose output, 3=very verbose output')
-            parser.add_argument('--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_argument('--pythonpath',
-                help='A directory to add to the Python path, e.g. "/home/djangoprojects/myproject".')
-            parser.add_argument('--traceback', action='store_true',
-                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)
+        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())
+        parser.add_argument('-v', '--verbosity', action='store', dest='verbosity', default='1',
+            type=int, choices=[0, 1, 2, 3],
+            help='Verbosity level; 0=minimal output, 1=normal output, 2=verbose output, 3=very verbose output')
+        parser.add_argument('--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_argument('--pythonpath',
+            help='A directory to add to the Python path, e.g. "/home/djangoprojects/myproject".')
+        parser.add_argument('--traceback', action='store_true',
+            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.")
+        self.add_arguments(parser)
         return parser
 
     def add_arguments(self, 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)
+        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', ())
         handle_default_options(options)
         try:
             self.execute(*args, **cmd_options)

+ 0 - 7
django/core/management/commands/test.py

@@ -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
docs/howto/custom-management-commands.txt

@@ -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
docs/releases/1.8.txt

@@ -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
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

+ 0 - 16
docs/topics/testing/advanced.txt

@@ -417,10 +417,6 @@ execute and tear down the test suite.
 
     .. versionchanged:: 1.8
 
-        Previously, you had to provide an ``option_list`` attribute to a
-        subclassed test runner to add options to the list of command-line
-        options that the :djadmin:`test` command could use.
-
         The ``keepdb``, ``reverse``, and ``debug_sql`` arguments were added.
 
 Attributes
@@ -448,18 +444,6 @@ Attributes
     By default it is set to ``unittest.defaultTestLoader``. You can override
     this attribute if your tests are going to be loaded in unusual ways.
 
-.. attribute:: DiscoverRunner.option_list
-
-    This is the tuple of ``optparse`` options which will be fed into the
-    management command's ``OptionParser`` for parsing arguments. See the
-    documentation for Python's ``optparse`` module for more details.
-
-    .. deprecated:: 1.8
-
-        You should now override the :meth:`~DiscoverRunner.add_arguments` class
-        method to add custom arguments accepted by the :djadmin:`test`
-        management command.
-
 Methods
 ~~~~~~~
 

+ 0 - 22
tests/user_commands/management/commands/optparse_cmd.py

@@ -1,22 +0,0 @@
-from optparse import make_option
-
-from django.core.management.base import BaseCommand
-
-
-class Command(BaseCommand):
-    help = "Test optparse compatibility."
-    args = ''
-
-    option_list = BaseCommand.option_list + (
-        make_option("-s", "--style", default="Rock'n'Roll"),
-        make_option("-x", "--example")
-    )
-
-    def handle(self, *args, **options):
-        options["example"]
-        # BaseCommand default option is available
-        options['verbosity']
-        assert (
-            isinstance(options['verbosity'], int), "verbosity option is not int, but %s" % type(options['verbosity'])
-        )
-        self.stdout.write("All right, let's dance %s." % options["style"])

+ 2 - 17
tests/user_commands/tests.py

@@ -5,11 +5,10 @@ from django.core import management
 from django.core.management import BaseCommand, CommandError, find_commands
 from django.core.management.utils import find_command, popen_wrapper
 from django.db import connection
-from django.test import SimpleTestCase, ignore_warnings, override_settings
-from django.test.utils import captured_stderr, captured_stdout, extend_sys_path
+from django.test import SimpleTestCase, override_settings
+from django.test.utils import captured_stderr, extend_sys_path
 from django.utils import translation
 from django.utils._os import upath
-from django.utils.deprecation import RemovedInDjango110Warning
 from django.utils.six import StringIO
 
 
@@ -104,20 +103,6 @@ class CommandTests(SimpleTestCase):
         self.assertNotIn("opt_3", out.getvalue())
         self.assertNotIn("opt-3", out.getvalue())
 
-    @ignore_warnings(category=RemovedInDjango110Warning)
-    def test_optparse_compatibility(self):
-        """
-        optparse should be supported during Django 1.8/1.9 releases.
-        """
-        out = StringIO()
-        management.call_command('optparse_cmd', stdout=out)
-        self.assertEqual(out.getvalue(), "All right, let's dance Rock'n'Roll.\n")
-
-        # Simulate command line execution
-        with captured_stdout() as stdout, captured_stderr():
-            management.execute_from_command_line(['django-admin', 'optparse_cmd'])
-        self.assertEqual(stdout.getvalue(), "All right, let's dance Rock'n'Roll.\n")
-
     def test_calling_a_command_with_only_empty_parameter_should_ends_gracefully(self):
         out = StringIO()
         management.call_command('hal', "--empty", stdout=out)