Skip to content

Commit 6eb3f53

Browse files
hramezanifelixxm
authored andcommitted
Fixed #32047 -- Fixed call_command() crash if a constant option from required mutually exclusive group is passed in options.
1 parent 11c4a44 commit 6eb3f53

File tree

3 files changed

+43
-4
lines changed

3 files changed

+43
-4
lines changed

django/core/management/__init__.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,9 @@
22
import os
33
import pkgutil
44
import sys
5-
from argparse import _SubParsersAction
5+
from argparse import (
6+
_AppendConstAction, _CountAction, _StoreConstAction, _SubParsersAction,
7+
)
68
from collections import defaultdict
79
from difflib import get_close_matches
810
from importlib import import_module
@@ -138,7 +140,9 @@ def get_actions(parser):
138140
# Any required arguments which are passed in via **options must be passed
139141
# to parse_args().
140142
parse_args += [
141-
'{}={}'.format(min(opt.option_strings), arg_options[opt.dest])
143+
min(opt.option_strings)
144+
if isinstance(opt, (_AppendConstAction, _CountAction, _StoreConstAction))
145+
else '{}={}'.format(min(opt.option_strings), arg_options[opt.dest])
142146
for opt in parser_actions if (
143147
opt.dest in options and
144148
(opt.required or opt in mutually_exclusive_required_options)

tests/user_commands/management/commands/mutually_exclusive_required.py

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,13 @@ def add_arguments(self, parser):
77
group = parser.add_mutually_exclusive_group(required=True)
88
group.add_argument('--foo-id', type=int, nargs='?', default=None)
99
group.add_argument('--foo-name', type=str, nargs='?', default=None)
10+
group.add_argument('--append_const', action='append_const', const=42)
11+
group.add_argument('--const', action='store_const', const=31)
12+
group.add_argument('--count', action='count')
13+
group.add_argument('--flag_false', action='store_false')
14+
group.add_argument('--flag_true', action='store_true')
1015

1116
def handle(self, *args, **options):
12-
self.stdout.write(','.join(options))
17+
for option, value in options.items():
18+
if value is not None:
19+
self.stdout.write('%s=%s' % (option, value))

tests/user_commands/tests.py

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -243,10 +243,38 @@ def test_mutually_exclusive_group_required_options(self):
243243
self.assertIn('foo_id', out.getvalue())
244244
management.call_command('mutually_exclusive_required', foo_name='foo', stdout=out)
245245
self.assertIn('foo_name', out.getvalue())
246-
msg = 'Error: one of the arguments --foo-id --foo-name is required'
246+
msg = (
247+
'Error: one of the arguments --foo-id --foo-name --append_const '
248+
'--const --count --flag_false --flag_true is required'
249+
)
247250
with self.assertRaisesMessage(CommandError, msg):
248251
management.call_command('mutually_exclusive_required', stdout=out)
249252

253+
def test_mutually_exclusive_group_required_const_options(self):
254+
tests = [
255+
('append_const', [42]),
256+
('const', 31),
257+
('count', 1),
258+
('flag_false', False),
259+
('flag_true', True),
260+
]
261+
for arg, value in tests:
262+
out = StringIO()
263+
expected_output = '%s=%s' % (arg, value)
264+
with self.subTest(arg=arg):
265+
management.call_command(
266+
'mutually_exclusive_required',
267+
'--%s' % arg,
268+
stdout=out,
269+
)
270+
self.assertIn(expected_output, out.getvalue())
271+
out.truncate(0)
272+
management.call_command(
273+
'mutually_exclusive_required',
274+
**{arg: value, 'stdout': out},
275+
)
276+
self.assertIn(expected_output, out.getvalue())
277+
250278
def test_subparser(self):
251279
out = StringIO()
252280
management.call_command('subparser', 'foo', 12, stdout=out)

0 commit comments

Comments
 (0)