Fixed #29295 -- Fixed management command crash when using subparsers.
Thanks Tim Graham for the fix.
This commit is contained in:
parent
21420096c4
commit
dd68b51e1d
|
@ -311,7 +311,7 @@ class ManagementUtility:
|
||||||
# Preprocess options to extract --settings and --pythonpath.
|
# Preprocess options to extract --settings and --pythonpath.
|
||||||
# These options could affect the commands that are available, so they
|
# These options could affect the commands that are available, so they
|
||||||
# must be processed early.
|
# must be processed early.
|
||||||
parser = CommandParser(None, usage="%(prog)s subcommand [options] [args]", add_help=False)
|
parser = CommandParser(usage='%(prog)s subcommand [options] [args]', add_help=False)
|
||||||
parser.add_argument('--settings')
|
parser.add_argument('--settings')
|
||||||
parser.add_argument('--pythonpath')
|
parser.add_argument('--pythonpath')
|
||||||
parser.add_argument('args', nargs='*') # catch-all
|
parser.add_argument('args', nargs='*') # catch-all
|
||||||
|
|
|
@ -42,19 +42,20 @@ class CommandParser(ArgumentParser):
|
||||||
SystemExit in several occasions, as SystemExit is unacceptable when a
|
SystemExit in several occasions, as SystemExit is unacceptable when a
|
||||||
command is called programmatically.
|
command is called programmatically.
|
||||||
"""
|
"""
|
||||||
def __init__(self, cmd, **kwargs):
|
def __init__(self, **kwargs):
|
||||||
self.cmd = cmd
|
self.missing_args_message = kwargs.pop('missing_args_message', None)
|
||||||
|
self.called_from_command_line = kwargs.pop('called_from_command_line', None)
|
||||||
super().__init__(**kwargs)
|
super().__init__(**kwargs)
|
||||||
|
|
||||||
def parse_args(self, args=None, namespace=None):
|
def parse_args(self, args=None, namespace=None):
|
||||||
# Catch missing argument for a better error message
|
# Catch missing argument for a better error message
|
||||||
if (hasattr(self.cmd, 'missing_args_message') and
|
if (self.missing_args_message and
|
||||||
not (args or any(not arg.startswith('-') for arg in args))):
|
not (args or any(not arg.startswith('-') for arg in args))):
|
||||||
self.error(self.cmd.missing_args_message)
|
self.error(self.missing_args_message)
|
||||||
return super().parse_args(args, namespace)
|
return super().parse_args(args, namespace)
|
||||||
|
|
||||||
def error(self, message):
|
def error(self, message):
|
||||||
if self.cmd._called_from_command_line:
|
if self.called_from_command_line:
|
||||||
super().error(message)
|
super().error(message)
|
||||||
else:
|
else:
|
||||||
raise CommandError("Error: %s" % message)
|
raise CommandError("Error: %s" % message)
|
||||||
|
@ -225,8 +226,10 @@ class BaseCommand:
|
||||||
parse the arguments to this command.
|
parse the arguments to this command.
|
||||||
"""
|
"""
|
||||||
parser = CommandParser(
|
parser = CommandParser(
|
||||||
self, prog="%s %s" % (os.path.basename(prog_name), subcommand),
|
prog='%s %s' % (os.path.basename(prog_name), subcommand),
|
||||||
description=self.help or None,
|
description=self.help or None,
|
||||||
|
missing_args_message=getattr(self, 'missing_args_message', None),
|
||||||
|
called_from_command_line=getattr(self, '_called_from_command_line', None),
|
||||||
)
|
)
|
||||||
# Add command-specific arguments first so that they appear in the
|
# Add command-specific arguments first so that they appear in the
|
||||||
# --help output before arguments common to all commands.
|
# --help output before arguments common to all commands.
|
||||||
|
|
|
@ -0,0 +1,12 @@
|
||||||
|
from django.core.management.base import BaseCommand
|
||||||
|
|
||||||
|
|
||||||
|
class Command(BaseCommand):
|
||||||
|
|
||||||
|
def add_arguments(self, parser):
|
||||||
|
subparsers = parser.add_subparsers()
|
||||||
|
parser_foo = subparsers.add_parser('foo')
|
||||||
|
parser_foo.add_argument('bar', type=int)
|
||||||
|
|
||||||
|
def handle(self, *args, **options):
|
||||||
|
self.stdout.write(','.join(options))
|
|
@ -206,6 +206,16 @@ class CommandTests(SimpleTestCase):
|
||||||
self.assertIn('need_me', out.getvalue())
|
self.assertIn('need_me', out.getvalue())
|
||||||
self.assertIn('needme2', out.getvalue())
|
self.assertIn('needme2', out.getvalue())
|
||||||
|
|
||||||
|
def test_subparser(self):
|
||||||
|
out = StringIO()
|
||||||
|
management.call_command('subparser', 'foo', 12, stdout=out)
|
||||||
|
self.assertIn('bar', out.getvalue())
|
||||||
|
|
||||||
|
def test_subparser_invalid_option(self):
|
||||||
|
msg = "Error: invalid choice: 'test' (choose from 'foo')"
|
||||||
|
with self.assertRaisesMessage(CommandError, msg):
|
||||||
|
management.call_command('subparser', 'test', 12)
|
||||||
|
|
||||||
|
|
||||||
class CommandRunTests(AdminScriptTestCase):
|
class CommandRunTests(AdminScriptTestCase):
|
||||||
"""
|
"""
|
||||||
|
|
Loading…
Reference in New Issue