summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorHasan Ramezani <hasan.r67@gmail.com>2020-10-29 19:30:43 +0100
committerMariusz Felisiak <felisiak.mariusz@gmail.com>2020-10-30 12:01:33 +0100
commitf06beea92999407cc8dad3c47f006b7c727095a6 (patch)
tree94ee9bc71037cd30cf3f49207f84901ffc8203af
parent966b5b49b6521483f1c90b4499c4c80e80136de3 (diff)
Fixed #32153 -- Fixed management commands when using required list options.
Thanks Mark Gajdosik for the report and initial patch.
-rw-r--r--django/core/management/__init__.py25
-rw-r--r--tests/user_commands/management/commands/mutually_exclusive_required.py1
-rw-r--r--tests/user_commands/management/commands/required_list_option.py10
-rw-r--r--tests/user_commands/tests.py21
4 files changed, 47 insertions, 10 deletions
diff --git a/django/core/management/__init__.py b/django/core/management/__init__.py
index aff9be4e85..1ba093e51f 100644
--- a/django/core/management/__init__.py
+++ b/django/core/management/__init__.py
@@ -120,7 +120,12 @@ def call_command(command_name, *args, **options):
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()}
- 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):
# 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
# to parse_args().
- parse_args += [
- min(opt.option_strings)
- if isinstance(opt, (_AppendConstAction, _CountAction, _StoreConstAction))
- else '{}={}'.format(min(opt.option_strings), arg_options[opt.dest])
- for opt in parser_actions if (
+ for opt in parser_actions:
+ if (
opt.dest in options and
(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 = dict(defaults._get_kwargs(), **arg_options)
# Raise an error if any unknown options were passed.
diff --git a/tests/user_commands/management/commands/mutually_exclusive_required.py b/tests/user_commands/management/commands/mutually_exclusive_required.py
index 3fbf514c4d..8e48f12658 100644
--- a/tests/user_commands/management/commands/mutually_exclusive_required.py
+++ b/tests/user_commands/management/commands/mutually_exclusive_required.py
@@ -7,6 +7,7 @@ class Command(BaseCommand):
group = parser.add_mutually_exclusive_group(required=True)
group.add_argument('--foo-id', type=int, 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('--const', action='store_const', const=31)
group.add_argument('--count', action='count')
diff --git a/tests/user_commands/management/commands/required_list_option.py b/tests/user_commands/management/commands/required_list_option.py
new file mode 100644
index 0000000000..84f39f0142
--- /dev/null
+++ b/tests/user_commands/management/commands/required_list_option.py
@@ -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))
diff --git a/tests/user_commands/tests.py b/tests/user_commands/tests.py
index eb2d38fbfb..9262e2717a 100644
--- a/tests/user_commands/tests.py
+++ b/tests/user_commands/tests.py
@@ -244,8 +244,9 @@ class CommandTests(SimpleTestCase):
management.call_command('mutually_exclusive_required', foo_name='foo', stdout=out)
self.assertIn('foo_name', out.getvalue())
msg = (
- 'Error: one of the arguments --foo-id --foo-name --append_const '
- '--const --count --flag_false --flag_true is required'
+ 'Error: one of the arguments --foo-id --foo-name --foo-list '
+ '--append_const --const --count --flag_false --flag_true is '
+ 'required'
)
with self.assertRaisesMessage(CommandError, msg):
management.call_command('mutually_exclusive_required', stdout=out)
@@ -275,6 +276,22 @@ class CommandTests(SimpleTestCase):
)
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):
args = {
'append_const': [42],