Refs #19973 -- Removed optparse support in management commands per deprecation timeline.
This commit is contained in:
parent
3bbebd06ad
commit
6a70cb5397
|
@ -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]
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
|
|
@ -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
|
||||
~~~~~~~
|
||||
|
||||
|
|
|
@ -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"])
|
|
@ -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)
|
||||
|
|
Loading…
Reference in New Issue