Fixed #19973 -- Replaced optparse by argparse in management commands
Thanks Tim Graham for the review.
This commit is contained in:
parent
79956d0694
commit
8568638603
|
@ -1,6 +1,7 @@
|
|||
from __future__ import unicode_literals
|
||||
|
||||
import collections
|
||||
from importlib import import_module
|
||||
from optparse import OptionParser, NO_DEFAULT
|
||||
import os
|
||||
import sys
|
||||
|
||||
|
@ -8,7 +9,8 @@ import django
|
|||
from django.apps import apps
|
||||
from django.conf import settings
|
||||
from django.core.exceptions import ImproperlyConfigured
|
||||
from django.core.management.base import BaseCommand, CommandError, handle_default_options
|
||||
from django.core.management.base import (BaseCommand, CommandError,
|
||||
CommandParser, handle_default_options)
|
||||
from django.core.management.color import color_style
|
||||
from django.utils import lru_cache
|
||||
from django.utils import six
|
||||
|
@ -93,78 +95,21 @@ def call_command(name, *args, **options):
|
|||
|
||||
if isinstance(app_name, BaseCommand):
|
||||
# If the command is already loaded, use it directly.
|
||||
klass = app_name
|
||||
command = app_name
|
||||
else:
|
||||
klass = load_command_class(app_name, name)
|
||||
command = load_command_class(app_name, name)
|
||||
|
||||
# Grab out a list of defaults from the options. optparse does this for us
|
||||
# when the script runs from the command line, but since call_command can
|
||||
# be called programmatically, we need to simulate the loading and handling
|
||||
# of defaults (see #10080 for details).
|
||||
defaults = {}
|
||||
for opt in klass.option_list:
|
||||
if opt.default is NO_DEFAULT:
|
||||
defaults[opt.dest] = None
|
||||
else:
|
||||
defaults[opt.dest] = opt.default
|
||||
defaults.update(options)
|
||||
# Simulate argument parsing to get the option defaults (see #10080 for details).
|
||||
parser = command.create_parser('', name)
|
||||
if command.use_argparse:
|
||||
defaults = parser.parse_args(args=args)
|
||||
defaults = dict(defaults._get_kwargs(), **options)
|
||||
else:
|
||||
# Legacy optparse method
|
||||
defaults, _ = parser.parse_args(args=[])
|
||||
defaults = dict(defaults.__dict__, **options)
|
||||
|
||||
return klass.execute(*args, **defaults)
|
||||
|
||||
|
||||
class LaxOptionParser(OptionParser):
|
||||
"""
|
||||
An option parser that doesn't raise any errors on unknown options.
|
||||
|
||||
This is needed because the --settings and --pythonpath options affect
|
||||
the commands (and thus the options) that are available to the user.
|
||||
"""
|
||||
def error(self, msg):
|
||||
pass
|
||||
|
||||
def print_help(self):
|
||||
"""Output nothing.
|
||||
|
||||
The lax options are included in the normal option parser, so under
|
||||
normal usage, we don't need to print the lax options.
|
||||
"""
|
||||
pass
|
||||
|
||||
def print_lax_help(self):
|
||||
"""Output the basic options available to every command.
|
||||
|
||||
This just redirects to the default print_help() behavior.
|
||||
"""
|
||||
OptionParser.print_help(self)
|
||||
|
||||
def _process_args(self, largs, rargs, values):
|
||||
"""
|
||||
Overrides OptionParser._process_args to exclusively handle default
|
||||
options and ignore args and other options.
|
||||
|
||||
This overrides the behavior of the super class, which stop parsing
|
||||
at the first unrecognized option.
|
||||
"""
|
||||
while rargs:
|
||||
arg = rargs[0]
|
||||
try:
|
||||
if arg[0:2] == "--" and len(arg) > 2:
|
||||
# process a single long option (possibly with value(s))
|
||||
# the superclass code pops the arg off rargs
|
||||
self._process_long_opt(rargs, values)
|
||||
elif arg[:1] == "-" and len(arg) > 1:
|
||||
# process a cluster of short options (possibly with
|
||||
# value(s) for the last one only)
|
||||
# the superclass code pops the arg off rargs
|
||||
self._process_short_opts(rargs, values)
|
||||
else:
|
||||
# it's either a non-default option or an arg
|
||||
# either way, add it to the args list so we can keep
|
||||
# dealing with options
|
||||
del rargs[0]
|
||||
raise Exception
|
||||
except: # Needed because we might need to catch a SystemExit
|
||||
largs.append(arg)
|
||||
return command.execute(*args, **defaults)
|
||||
|
||||
|
||||
class ManagementUtility(object):
|
||||
|
@ -296,8 +241,13 @@ class ManagementUtility(object):
|
|||
# Fail silently if DJANGO_SETTINGS_MODULE isn't set. The
|
||||
# user will find out once they execute the command.
|
||||
pass
|
||||
options += [(s_opt.get_opt_string(), s_opt.nargs) for s_opt in
|
||||
subcommand_cls.option_list]
|
||||
parser = subcommand_cls.create_parser('', cwords[0])
|
||||
if subcommand_cls.use_argparse:
|
||||
options += [(sorted(s_opt.option_strings)[0], s_opt.nargs != 0) for s_opt in
|
||||
parser._actions if s_opt.option_strings]
|
||||
else:
|
||||
options += [(s_opt.get_opt_string(), s_opt.nargs) for s_opt in
|
||||
parser.option_list]
|
||||
# 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]
|
||||
|
@ -317,23 +267,24 @@ class ManagementUtility(object):
|
|||
Given the command-line arguments, this figures out which subcommand is
|
||||
being run, creates a parser appropriate to that command, and runs it.
|
||||
"""
|
||||
# Preprocess options to extract --settings and --pythonpath.
|
||||
# These options could affect the commands that are available, so they
|
||||
# must be processed early.
|
||||
parser = LaxOptionParser(usage="%prog subcommand [options] [args]",
|
||||
version=django.get_version(),
|
||||
option_list=BaseCommand.option_list)
|
||||
try:
|
||||
options, args = parser.parse_args(self.argv)
|
||||
handle_default_options(options)
|
||||
except: # Needed because parser.parse_args can raise SystemExit
|
||||
pass # Ignore any option errors at this point.
|
||||
|
||||
try:
|
||||
subcommand = self.argv[1]
|
||||
except IndexError:
|
||||
subcommand = 'help' # Display help if no arguments were given.
|
||||
|
||||
# Preprocess options to extract --settings and --pythonpath.
|
||||
# These options could affect the commands that are available, so they
|
||||
# must be processed early.
|
||||
parser = CommandParser(None, usage="%(prog)s subcommand [options] [args]", add_help=False)
|
||||
parser.add_argument('--settings')
|
||||
parser.add_argument('--pythonpath')
|
||||
parser.add_argument('args', nargs='*') # catch-all
|
||||
try:
|
||||
options, args = parser.parse_known_args(self.argv[2:])
|
||||
handle_default_options(options)
|
||||
except CommandError:
|
||||
pass # Ignore any option errors at this point.
|
||||
|
||||
no_settings_commands = [
|
||||
'help', 'version', '--help', '--version', '-h',
|
||||
'compilemessages', 'makemessages',
|
||||
|
@ -355,22 +306,17 @@ class ManagementUtility(object):
|
|||
self.autocomplete()
|
||||
|
||||
if subcommand == 'help':
|
||||
if len(args) <= 2:
|
||||
parser.print_lax_help()
|
||||
sys.stdout.write(self.main_help_text() + '\n')
|
||||
elif args[2] == '--commands':
|
||||
if '--commands' in args:
|
||||
sys.stdout.write(self.main_help_text(commands_only=True) + '\n')
|
||||
elif len(options.args) < 1:
|
||||
sys.stdout.write(self.main_help_text() + '\n')
|
||||
else:
|
||||
self.fetch_command(args[2]).print_help(self.prog_name, args[2])
|
||||
elif subcommand == 'version':
|
||||
sys.stdout.write(parser.get_version() + '\n')
|
||||
self.fetch_command(options.args[0]).print_help(self.prog_name, options.args[0])
|
||||
# Special-cases: We want 'django-admin.py --version' and
|
||||
# 'django-admin.py --help' to work, for backwards compatibility.
|
||||
elif self.argv[1:] == ['--version']:
|
||||
# LaxOptionParser already takes care of printing the version.
|
||||
pass
|
||||
elif subcommand == 'version' or self.argv[1:] == ['--version']:
|
||||
sys.stdout.write(django.get_version() + '\n')
|
||||
elif self.argv[1:] in (['--help'], ['-h']):
|
||||
parser.print_lax_help()
|
||||
sys.stdout.write(self.main_help_text() + '\n')
|
||||
else:
|
||||
self.fetch_command(subcommand).run_from_argv(self.argv)
|
||||
|
|
|
@ -11,13 +11,14 @@ import os
|
|||
import sys
|
||||
import warnings
|
||||
|
||||
from optparse import make_option, OptionParser
|
||||
from argparse import ArgumentParser
|
||||
from optparse import OptionParser
|
||||
|
||||
import django
|
||||
from django.core import checks
|
||||
from django.core.exceptions import ImproperlyConfigured
|
||||
from django.core.management.color import color_style, no_style
|
||||
from django.utils.deprecation import RemovedInDjango19Warning
|
||||
from django.utils.deprecation import RemovedInDjango19Warning, RemovedInDjango20Warning
|
||||
from django.utils.encoding import force_str
|
||||
|
||||
|
||||
|
@ -37,6 +38,27 @@ class CommandError(Exception):
|
|||
pass
|
||||
|
||||
|
||||
class CommandParser(ArgumentParser):
|
||||
"""
|
||||
Customized ArgumentParser class to improve some error messages and prevent
|
||||
SystemExit in several occasions, as SystemExit is unacceptable when a
|
||||
command is called programmatically.
|
||||
"""
|
||||
def __init__(self, cmd, **kwargs):
|
||||
self.cmd = cmd
|
||||
super(CommandParser, self).__init__(**kwargs)
|
||||
|
||||
def parse_args(self, args=None, namespace=None):
|
||||
# Catch missing argument for a better error message
|
||||
if (hasattr(self.cmd, 'missing_args_message') and
|
||||
not (args or any([not arg.startswith('-') for arg in args]))):
|
||||
raise CommandError("Error: %s" % self.cmd.missing_args_message)
|
||||
return super(CommandParser, self).parse_args(args, namespace)
|
||||
|
||||
def error(self, message):
|
||||
raise CommandError("Error: %s" % message)
|
||||
|
||||
|
||||
def handle_default_options(options):
|
||||
"""
|
||||
Include any default options that all commands should accept here
|
||||
|
@ -91,7 +113,7 @@ class BaseCommand(object):
|
|||
and calls its ``run_from_argv()`` method.
|
||||
|
||||
2. The ``run_from_argv()`` method calls ``create_parser()`` to get
|
||||
an ``OptionParser`` for the arguments, parses them, performs
|
||||
an ``ArgumentParser`` for the arguments, parses them, performs
|
||||
any environment changes requested by options like
|
||||
``pythonpath``, and then calls the ``execute()`` method,
|
||||
passing the parsed arguments.
|
||||
|
@ -133,6 +155,7 @@ class BaseCommand(object):
|
|||
``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 2.0.
|
||||
|
||||
``output_transaction``
|
||||
A boolean indicating whether the command outputs SQL
|
||||
|
@ -180,19 +203,7 @@ class BaseCommand(object):
|
|||
settings. This condition will generate a CommandError.
|
||||
"""
|
||||
# Metadata about this command.
|
||||
option_list = (
|
||||
make_option('-v', '--verbosity', action='store', dest='verbosity', default='1',
|
||||
type='choice', choices=['0', '1', '2', '3'],
|
||||
help='Verbosity level; 0=minimal output, 1=normal output, 2=verbose output, 3=very verbose output'),
|
||||
make_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.'),
|
||||
make_option('--pythonpath',
|
||||
help='A directory to add to the Python path, e.g. "/home/djangoprojects/myproject".'),
|
||||
make_option('--traceback', action='store_true',
|
||||
help='Raise on exception'),
|
||||
make_option('--no-color', action='store_true', dest='no_color', default=False,
|
||||
help="Don't colorize the command output."),
|
||||
)
|
||||
option_list = ()
|
||||
help = ''
|
||||
args = ''
|
||||
|
||||
|
@ -232,6 +243,10 @@ class BaseCommand(object):
|
|||
self.requires_model_validation if has_old_option else
|
||||
True)
|
||||
|
||||
@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,14 +270,56 @@ class BaseCommand(object):
|
|||
|
||||
def create_parser(self, prog_name, subcommand):
|
||||
"""
|
||||
Create and return the ``OptionParser`` which will be used to
|
||||
Create and return the ``ArgumentParser`` which will be used to
|
||||
parse the arguments to this command.
|
||||
|
||||
"""
|
||||
return OptionParser(prog=prog_name,
|
||||
usage=self.usage(subcommand),
|
||||
version=self.get_version(),
|
||||
option_list=self.option_list)
|
||||
if not self.use_argparse:
|
||||
# Backwards compatibility: use deprecated optparse module
|
||||
warnings.warn("OptionParser usage for Django management commands "
|
||||
"is deprecated, use ArgumentParser instead",
|
||||
RemovedInDjango20Warning)
|
||||
parser = OptionParser(prog=prog_name,
|
||||
usage=self.usage(subcommand),
|
||||
version=self.get_version())
|
||||
parser.add_option('-v', '--verbosity', action='store', dest='verbosity', default='1',
|
||||
type='choice', choices=['0', '1', '2', '3'],
|
||||
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 exception')
|
||||
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" % (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 exception')
|
||||
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
|
||||
|
||||
def add_arguments(self, parser):
|
||||
"""
|
||||
Entry point for subclassed commands to add custom arguments.
|
||||
"""
|
||||
pass
|
||||
|
||||
def print_help(self, prog_name, subcommand):
|
||||
"""
|
||||
|
@ -282,10 +339,22 @@ class BaseCommand(object):
|
|||
``Exception`` is not ``CommandError``, raise it.
|
||||
"""
|
||||
parser = self.create_parser(argv[0], argv[1])
|
||||
options, args = parser.parse_args(argv[2:])
|
||||
|
||||
if self.use_argparse:
|
||||
options = parser.parse_args(argv[2:])
|
||||
cmd_options = vars(options)
|
||||
# Move positional args out of options to mimic legacy optparse
|
||||
if 'args' in options:
|
||||
args = options.args
|
||||
del cmd_options['args']
|
||||
else:
|
||||
args = ()
|
||||
else:
|
||||
options, args = parser.parse_args(argv[2:])
|
||||
cmd_options = vars(options)
|
||||
handle_default_options(options)
|
||||
try:
|
||||
self.execute(*args, **options.__dict__)
|
||||
self.execute(*args, **cmd_options)
|
||||
except Exception as e:
|
||||
if options.traceback or not isinstance(e, CommandError):
|
||||
raise
|
||||
|
@ -433,12 +502,14 @@ class AppCommand(BaseCommand):
|
|||
Rather than implementing ``handle()``, subclasses must implement
|
||||
``handle_app_config()``, which will be called once for each application.
|
||||
"""
|
||||
args = '<app_label app_label ...>'
|
||||
missing_args_message = "Enter at least one application label."
|
||||
|
||||
def add_arguments(self, parser):
|
||||
parser.add_argument('args', metavar='app_label', nargs='+',
|
||||
help='One or more application label.')
|
||||
|
||||
def handle(self, *app_labels, **options):
|
||||
from django.apps import apps
|
||||
if not app_labels:
|
||||
raise CommandError("Enter at least one application label.")
|
||||
try:
|
||||
app_configs = [apps.get_app_config(app_label) for app_label in app_labels]
|
||||
except (LookupError, ImportError) as e:
|
||||
|
@ -490,13 +561,13 @@ class LabelCommand(BaseCommand):
|
|||
``AppCommand`` instead.
|
||||
|
||||
"""
|
||||
args = '<label label ...>'
|
||||
label = 'label'
|
||||
missing_args_message = "Enter at least one %s." % label
|
||||
|
||||
def add_arguments(self, parser):
|
||||
parser.add_argument('args', metavar=self.label, nargs='+')
|
||||
|
||||
def handle(self, *labels, **options):
|
||||
if not labels:
|
||||
raise CommandError('Enter at least one %s.' % self.label)
|
||||
|
||||
output = []
|
||||
for label in labels:
|
||||
label_output = self.handle_label(label, **options)
|
||||
|
|
|
@ -50,13 +50,11 @@ class Command(BaseCommand):
|
|||
super(Command, self).run_from_argv(argv)
|
||||
|
||||
def create_parser(self, prog_name, subcommand):
|
||||
parser = super(Command, self).create_parser(prog_name, subcommand)
|
||||
test_runner_class = get_runner(settings, self.test_runner)
|
||||
options = self.option_list + getattr(
|
||||
test_runner_class, 'option_list', ())
|
||||
return OptionParser(prog=prog_name,
|
||||
usage=self.usage(subcommand),
|
||||
version=self.get_version(),
|
||||
option_list=options)
|
||||
for opt in getattr(test_runner_class, 'option_list', ()):
|
||||
parser.add_option(opt)
|
||||
return parser
|
||||
|
||||
def execute(self, *args, **options):
|
||||
if int(options['verbosity']) > 0:
|
||||
|
|
|
@ -4,7 +4,6 @@ from django.core.management.base import AppCommand
|
|||
class Command(AppCommand):
|
||||
help = 'Test Application-based commands'
|
||||
requires_system_checks = False
|
||||
args = '[app_label ...]'
|
||||
|
||||
def handle_app_config(self, app_config, **options):
|
||||
print('EXECUTE:AppCommand name=%s, options=%s' % (app_config.name, sorted(options.items())))
|
||||
|
|
|
@ -930,21 +930,21 @@ class ManageAlternateSettings(AdminScriptTestCase):
|
|||
"alternate: manage.py can execute user commands if settings are provided as argument"
|
||||
args = ['noargs_command', '--settings=alternate_settings']
|
||||
out, err = self.run_manage(args)
|
||||
self.assertOutput(out, str_prefix("EXECUTE:NoArgsCommand options=[('no_color', False), ('pythonpath', None), ('settings', 'alternate_settings'), ('traceback', None), ('verbosity', %(_)s'1')]"))
|
||||
self.assertOutput(out, "EXECUTE:NoArgsCommand options=[('no_color', False), ('pythonpath', None), ('settings', 'alternate_settings'), ('traceback', False), ('verbosity', 1)]")
|
||||
self.assertNoOutput(err)
|
||||
|
||||
def test_custom_command_with_environment(self):
|
||||
"alternate: manage.py can execute user commands if settings are provided in environment"
|
||||
args = ['noargs_command']
|
||||
out, err = self.run_manage(args, 'alternate_settings')
|
||||
self.assertOutput(out, str_prefix("EXECUTE:NoArgsCommand options=[('no_color', False), ('pythonpath', None), ('settings', None), ('traceback', None), ('verbosity', %(_)s'1')]"))
|
||||
self.assertOutput(out, "EXECUTE:NoArgsCommand options=[('no_color', False), ('pythonpath', None), ('settings', None), ('traceback', False), ('verbosity', 1)]")
|
||||
self.assertNoOutput(err)
|
||||
|
||||
def test_custom_command_output_color(self):
|
||||
"alternate: manage.py output syntax color can be deactivated with the `--no-color` option"
|
||||
args = ['noargs_command', '--no-color', '--settings=alternate_settings']
|
||||
out, err = self.run_manage(args)
|
||||
self.assertOutput(out, str_prefix("EXECUTE:NoArgsCommand options=[('no_color', True), ('pythonpath', None), ('settings', 'alternate_settings'), ('traceback', None), ('verbosity', %(_)s'1')]"))
|
||||
self.assertOutput(out, "EXECUTE:NoArgsCommand options=[('no_color', True), ('pythonpath', None), ('settings', 'alternate_settings'), ('traceback', False), ('verbosity', 1)]")
|
||||
self.assertNoOutput(err)
|
||||
|
||||
|
||||
|
@ -1340,13 +1340,13 @@ class CommandTypes(AdminScriptTestCase):
|
|||
def test_version_alternative(self):
|
||||
"--version is equivalent to version"
|
||||
args1, args2 = ['version'], ['--version']
|
||||
self.assertEqual(self.run_manage(args1), self.run_manage(args2))
|
||||
# It's possible one outputs on stderr and the other on stdout, hence the set
|
||||
self.assertEqual(set(self.run_manage(args1)), set(self.run_manage(args2)))
|
||||
|
||||
def test_help(self):
|
||||
"help is handled as a special case"
|
||||
args = ['help']
|
||||
out, err = self.run_manage(args)
|
||||
self.assertOutput(out, "Usage: manage.py subcommand [options] [args]")
|
||||
self.assertOutput(out, "Type 'manage.py help <subcommand>' for help on a specific subcommand.")
|
||||
self.assertOutput(out, '[django]')
|
||||
self.assertOutput(out, 'startapp')
|
||||
|
@ -1356,7 +1356,7 @@ class CommandTypes(AdminScriptTestCase):
|
|||
"help --commands shows the list of all available commands"
|
||||
args = ['help', '--commands']
|
||||
out, err = self.run_manage(args)
|
||||
self.assertNotInOutput(out, 'Usage:')
|
||||
self.assertNotInOutput(out, 'usage:')
|
||||
self.assertNotInOutput(out, 'Options:')
|
||||
self.assertNotInOutput(out, '[django]')
|
||||
self.assertOutput(out, 'startapp')
|
||||
|
@ -1489,13 +1489,13 @@ class CommandTypes(AdminScriptTestCase):
|
|||
args = ['noargs_command']
|
||||
out, err = self.run_manage(args)
|
||||
self.assertNoOutput(err)
|
||||
self.assertOutput(out, str_prefix("EXECUTE:NoArgsCommand options=[('no_color', False), ('pythonpath', None), ('settings', None), ('traceback', None), ('verbosity', %(_)s'1')]"))
|
||||
self.assertOutput(out, "EXECUTE:NoArgsCommand options=[('no_color', False), ('pythonpath', None), ('settings', None), ('traceback', False), ('verbosity', 1)]")
|
||||
|
||||
def test_noargs_with_args(self):
|
||||
"NoArg Commands raise an error if an argument is provided"
|
||||
args = ['noargs_command', 'argument']
|
||||
out, err = self.run_manage(args)
|
||||
self.assertOutput(err, "Error: Command doesn't accept any arguments")
|
||||
self.assertOutput(err, "Error: unrecognized arguments: argument")
|
||||
|
||||
def test_app_command(self):
|
||||
"User AppCommands can execute when a single app name is provided"
|
||||
|
@ -1503,7 +1503,7 @@ class CommandTypes(AdminScriptTestCase):
|
|||
out, err = self.run_manage(args)
|
||||
self.assertNoOutput(err)
|
||||
self.assertOutput(out, "EXECUTE:AppCommand name=django.contrib.auth, options=")
|
||||
self.assertOutput(out, str_prefix(", options=[('no_color', False), ('pythonpath', None), ('settings', None), ('traceback', None), ('verbosity', %(_)s'1')]"))
|
||||
self.assertOutput(out, ", options=[('no_color', False), ('pythonpath', None), ('settings', None), ('traceback', False), ('verbosity', 1)]")
|
||||
|
||||
def test_app_command_no_apps(self):
|
||||
"User AppCommands raise an error when no app name is provided"
|
||||
|
@ -1517,9 +1517,9 @@ class CommandTypes(AdminScriptTestCase):
|
|||
out, err = self.run_manage(args)
|
||||
self.assertNoOutput(err)
|
||||
self.assertOutput(out, "EXECUTE:AppCommand name=django.contrib.auth, options=")
|
||||
self.assertOutput(out, str_prefix(", options=[('no_color', False), ('pythonpath', None), ('settings', None), ('traceback', None), ('verbosity', %(_)s'1')]"))
|
||||
self.assertOutput(out, ", options=[('no_color', False), ('pythonpath', None), ('settings', None), ('traceback', False), ('verbosity', 1)]")
|
||||
self.assertOutput(out, "EXECUTE:AppCommand name=django.contrib.contenttypes, options=")
|
||||
self.assertOutput(out, str_prefix(", options=[('no_color', False), ('pythonpath', None), ('settings', None), ('traceback', None), ('verbosity', %(_)s'1')]"))
|
||||
self.assertOutput(out, ", options=[('no_color', False), ('pythonpath', None), ('settings', None), ('traceback', False), ('verbosity', 1)]")
|
||||
|
||||
def test_app_command_invalid_app_label(self):
|
||||
"User AppCommands can execute when a single app name is provided"
|
||||
|
@ -1538,7 +1538,7 @@ class CommandTypes(AdminScriptTestCase):
|
|||
args = ['label_command', 'testlabel']
|
||||
out, err = self.run_manage(args)
|
||||
self.assertNoOutput(err)
|
||||
self.assertOutput(out, str_prefix("EXECUTE:LabelCommand label=testlabel, options=[('no_color', False), ('pythonpath', None), ('settings', None), ('traceback', None), ('verbosity', %(_)s'1')]"))
|
||||
self.assertOutput(out, "EXECUTE:LabelCommand label=testlabel, options=[('no_color', False), ('pythonpath', None), ('settings', None), ('traceback', False), ('verbosity', 1)]")
|
||||
|
||||
def test_label_command_no_label(self):
|
||||
"User LabelCommands raise an error if no label is provided"
|
||||
|
@ -1551,8 +1551,8 @@ class CommandTypes(AdminScriptTestCase):
|
|||
args = ['label_command', 'testlabel', 'anotherlabel']
|
||||
out, err = self.run_manage(args)
|
||||
self.assertNoOutput(err)
|
||||
self.assertOutput(out, str_prefix("EXECUTE:LabelCommand label=testlabel, options=[('no_color', False), ('pythonpath', None), ('settings', None), ('traceback', None), ('verbosity', %(_)s'1')]"))
|
||||
self.assertOutput(out, str_prefix("EXECUTE:LabelCommand label=anotherlabel, options=[('no_color', False), ('pythonpath', None), ('settings', None), ('traceback', None), ('verbosity', %(_)s'1')]"))
|
||||
self.assertOutput(out, "EXECUTE:LabelCommand label=testlabel, options=[('no_color', False), ('pythonpath', None), ('settings', None), ('traceback', False), ('verbosity', 1)]")
|
||||
self.assertOutput(out, "EXECUTE:LabelCommand label=anotherlabel, options=[('no_color', False), ('pythonpath', None), ('settings', None), ('traceback', False), ('verbosity', 1)]")
|
||||
|
||||
def test_requires_model_validation_and_requires_system_checks_both_defined(self):
|
||||
with warnings.catch_warnings(record=True):
|
||||
|
@ -1587,8 +1587,8 @@ class ArgumentOrder(AdminScriptTestCase):
|
|||
"""Tests for 2-stage argument parsing scheme.
|
||||
|
||||
django-admin command arguments are parsed in 2 parts; the core arguments
|
||||
(--settings, --traceback and --pythonpath) are parsed using a Lax parser.
|
||||
This Lax parser ignores any unknown options. Then the full settings are
|
||||
(--settings, --traceback and --pythonpath) are parsed using a basic parser,
|
||||
ignoring any unknown options. Then the full settings are
|
||||
passed to the command parser, which extracts commands of interest to the
|
||||
individual command.
|
||||
"""
|
||||
|
|
|
@ -46,13 +46,13 @@ class BashCompletionTests(unittest.TestCase):
|
|||
|
||||
def test_django_admin_py(self):
|
||||
"django_admin.py will autocomplete option flags"
|
||||
self._user_input('django-admin.py sqlall --v')
|
||||
self._user_input('django-admin.py sqlall --verb')
|
||||
output = self._run_autocomplete()
|
||||
self.assertEqual(output, ['--verbosity='])
|
||||
|
||||
def test_manage_py(self):
|
||||
"manage.py will autocomplete option flags"
|
||||
self._user_input('manage.py sqlall --v')
|
||||
self._user_input('manage.py sqlall --verb')
|
||||
output = self._run_autocomplete()
|
||||
self.assertEqual(output, ['--verbosity='])
|
||||
|
||||
|
|
|
@ -0,0 +1,19 @@
|
|||
from optparse import make_option
|
||||
|
||||
from django.core.management.base import BaseCommand, CommandError
|
||||
|
||||
|
||||
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):
|
||||
example = options["example"]
|
||||
# BaseCommand default option is available
|
||||
options['verbosity']
|
||||
self.stdout.write("All right, let's dance %s." % options["style"])
|
|
@ -74,6 +74,24 @@ class CommandTests(SimpleTestCase):
|
|||
if current_path is not None:
|
||||
os.environ['PATH'] = current_path
|
||||
|
||||
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
|
||||
old_stdout, old_stderr = sys.stdout, sys.stderr
|
||||
sys.stdout, sys.stderr = StringIO(), StringIO()
|
||||
try:
|
||||
management.execute_from_command_line(['django-admin', 'optparse_cmd'])
|
||||
finally:
|
||||
output = sys.stdout.getvalue()
|
||||
sys.stdout, sys.stderr = old_stdout, old_stderr
|
||||
self.assertEqual(output, "All right, let's dance Rock'n'Roll.\n")
|
||||
|
||||
|
||||
class UtilsTests(SimpleTestCase):
|
||||
|
||||
|
|
Loading…
Reference in New Issue