Fixed #32153 -- Fixed management commands when using required list options.
Thanks Mark Gajdosik for the report and initial patch.
This commit is contained in:
parent
966b5b49b6
commit
f06beea929
|
@ -120,7 +120,12 @@ def call_command(command_name, *args, **options):
|
||||||
for s_opt in parser._actions if s_opt.option_strings
|
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()}
|
arg_options = {opt_mapping.get(key, key): value for key, value in options.items()}
|
||||||
parse_args = [str(a) for a in args]
|
parse_args = []
|
||||||
|
for arg in args:
|
||||||
|
if isinstance(arg, (list, tuple)):
|
||||||
|
parse_args += map(str, arg)
|
||||||
|
else:
|
||||||
|
parse_args.append(str(arg))
|
||||||
|
|
||||||
def get_actions(parser):
|
def get_actions(parser):
|
||||||
# Parser actions and actions from sub-parser choices.
|
# Parser actions and actions from sub-parser choices.
|
||||||
|
@ -139,15 +144,19 @@ def call_command(command_name, *args, **options):
|
||||||
}
|
}
|
||||||
# Any required arguments which are passed in via **options must be passed
|
# Any required arguments which are passed in via **options must be passed
|
||||||
# to parse_args().
|
# to parse_args().
|
||||||
parse_args += [
|
for opt in parser_actions:
|
||||||
min(opt.option_strings)
|
if (
|
||||||
if isinstance(opt, (_AppendConstAction, _CountAction, _StoreConstAction))
|
|
||||||
else '{}={}'.format(min(opt.option_strings), arg_options[opt.dest])
|
|
||||||
for opt in parser_actions if (
|
|
||||||
opt.dest in options and
|
opt.dest in options and
|
||||||
(opt.required or opt in mutually_exclusive_required_options)
|
(opt.required or opt in mutually_exclusive_required_options)
|
||||||
)
|
):
|
||||||
]
|
parse_args.append(min(opt.option_strings))
|
||||||
|
if isinstance(opt, (_AppendConstAction, _CountAction, _StoreConstAction)):
|
||||||
|
continue
|
||||||
|
value = arg_options[opt.dest]
|
||||||
|
if isinstance(value, (list, tuple)):
|
||||||
|
parse_args += map(str, value)
|
||||||
|
else:
|
||||||
|
parse_args.append(str(value))
|
||||||
defaults = parser.parse_args(args=parse_args)
|
defaults = parser.parse_args(args=parse_args)
|
||||||
defaults = dict(defaults._get_kwargs(), **arg_options)
|
defaults = dict(defaults._get_kwargs(), **arg_options)
|
||||||
# Raise an error if any unknown options were passed.
|
# Raise an error if any unknown options were passed.
|
||||||
|
|
|
@ -7,6 +7,7 @@ class Command(BaseCommand):
|
||||||
group = parser.add_mutually_exclusive_group(required=True)
|
group = parser.add_mutually_exclusive_group(required=True)
|
||||||
group.add_argument('--foo-id', type=int, nargs='?', default=None)
|
group.add_argument('--foo-id', type=int, nargs='?', default=None)
|
||||||
group.add_argument('--foo-name', type=str, nargs='?', default=None)
|
group.add_argument('--foo-name', type=str, nargs='?', default=None)
|
||||||
|
group.add_argument('--foo-list', type=int, nargs='+')
|
||||||
group.add_argument('--append_const', action='append_const', const=42)
|
group.add_argument('--append_const', action='append_const', const=42)
|
||||||
group.add_argument('--const', action='store_const', const=31)
|
group.add_argument('--const', action='store_const', const=31)
|
||||||
group.add_argument('--count', action='count')
|
group.add_argument('--count', action='count')
|
||||||
|
|
|
@ -0,0 +1,10 @@
|
||||||
|
from django.core.management.base import BaseCommand
|
||||||
|
|
||||||
|
|
||||||
|
class Command(BaseCommand):
|
||||||
|
def add_arguments(self, parser):
|
||||||
|
parser.add_argument('--foo-list', nargs='+', type=int, required=True)
|
||||||
|
|
||||||
|
def handle(self, *args, **options):
|
||||||
|
for option, value in options.items():
|
||||||
|
self.stdout.write('%s=%s' % (option, value))
|
|
@ -244,8 +244,9 @@ class CommandTests(SimpleTestCase):
|
||||||
management.call_command('mutually_exclusive_required', foo_name='foo', stdout=out)
|
management.call_command('mutually_exclusive_required', foo_name='foo', stdout=out)
|
||||||
self.assertIn('foo_name', out.getvalue())
|
self.assertIn('foo_name', out.getvalue())
|
||||||
msg = (
|
msg = (
|
||||||
'Error: one of the arguments --foo-id --foo-name --append_const '
|
'Error: one of the arguments --foo-id --foo-name --foo-list '
|
||||||
'--const --count --flag_false --flag_true is required'
|
'--append_const --const --count --flag_false --flag_true is '
|
||||||
|
'required'
|
||||||
)
|
)
|
||||||
with self.assertRaisesMessage(CommandError, msg):
|
with self.assertRaisesMessage(CommandError, msg):
|
||||||
management.call_command('mutually_exclusive_required', stdout=out)
|
management.call_command('mutually_exclusive_required', stdout=out)
|
||||||
|
@ -275,6 +276,22 @@ class CommandTests(SimpleTestCase):
|
||||||
)
|
)
|
||||||
self.assertIn(expected_output, out.getvalue())
|
self.assertIn(expected_output, out.getvalue())
|
||||||
|
|
||||||
|
def test_required_list_option(self):
|
||||||
|
tests = [
|
||||||
|
(('--foo-list', [1, 2]), {}),
|
||||||
|
((), {'foo_list': [1, 2]}),
|
||||||
|
]
|
||||||
|
for command in ['mutually_exclusive_required', 'required_list_option']:
|
||||||
|
for args, kwargs in tests:
|
||||||
|
with self.subTest(command=command, args=args, kwargs=kwargs):
|
||||||
|
out = StringIO()
|
||||||
|
management.call_command(
|
||||||
|
command,
|
||||||
|
*args,
|
||||||
|
**{**kwargs, 'stdout': out},
|
||||||
|
)
|
||||||
|
self.assertIn('foo_list=[1, 2]', out.getvalue())
|
||||||
|
|
||||||
def test_required_const_options(self):
|
def test_required_const_options(self):
|
||||||
args = {
|
args = {
|
||||||
'append_const': [42],
|
'append_const': [42],
|
||||||
|
|
Loading…
Reference in New Issue