diff options
| author | Hasan Ramezani <hasan.r67@gmail.com> | 2021-10-22 16:38:14 +0200 |
|---|---|---|
| committer | Mariusz Felisiak <felisiak.mariusz@gmail.com> | 2021-10-25 07:48:06 +0200 |
| commit | c1e4111c74ee9d9f48cbee5a5b7c40289203c93d (patch) | |
| tree | 24d45f9bf4b7f54471fa71f6b7e764e5d38fd778 | |
| parent | 1feb55d60736011ee94fbff9ba0c1c25acfd0b14 (diff) | |
Fixed #33205 -- Made call_command() raise TypeError when dest with multiple arguments is passed.
| -rw-r--r-- | django/core/management/__init__.py | 6 | ||||
| -rw-r--r-- | tests/user_commands/management/commands/mutually_exclusive_required_with_same_dest.py | 13 | ||||
| -rw-r--r-- | tests/user_commands/tests.py | 35 |
3 files changed, 54 insertions, 0 deletions
diff --git a/django/core/management/__init__.py b/django/core/management/__init__.py index 4e30a28f33..049297b5aa 100644 --- a/django/core/management/__init__.py +++ b/django/core/management/__init__.py @@ -149,6 +149,12 @@ def call_command(command_name, *args, **options): opt.dest in options and (opt.required or opt in mutually_exclusive_required_options) ): + opt_dest_count = sum(v == opt.dest for v in opt_mapping.values()) + if opt_dest_count > 1: + raise TypeError( + f'Cannot pass the dest {opt.dest!r} that matches multiple ' + f'arguments via **options.' + ) parse_args.append(min(opt.option_strings)) if isinstance(opt, (_AppendConstAction, _CountAction, _StoreConstAction)): continue diff --git a/tests/user_commands/management/commands/mutually_exclusive_required_with_same_dest.py b/tests/user_commands/management/commands/mutually_exclusive_required_with_same_dest.py new file mode 100644 index 0000000000..1a9ab5576d --- /dev/null +++ b/tests/user_commands/management/commands/mutually_exclusive_required_with_same_dest.py @@ -0,0 +1,13 @@ +from django.core.management.base import BaseCommand + + +class Command(BaseCommand): + def add_arguments(self, parser): + group = parser.add_mutually_exclusive_group(required=True) + group.add_argument('--for', dest='until', action='store') + group.add_argument('--until', action='store') + + def handle(self, *args, **options): + for option, value in options.items(): + if value is not None: + self.stdout.write('%s=%s' % (option, value)) diff --git a/tests/user_commands/tests.py b/tests/user_commands/tests.py index d3afb697bb..8da4e887d3 100644 --- a/tests/user_commands/tests.py +++ b/tests/user_commands/tests.py @@ -274,6 +274,41 @@ class CommandTests(SimpleTestCase): ) self.assertIn(expected_output, out.getvalue()) + def test_mutually_exclusive_group_required_with_same_dest_options(self): + tests = [ + {'until': '2'}, + {'for': '1', 'until': '2'}, + ] + msg = ( + "Cannot pass the dest 'until' that matches multiple arguments via " + "**options." + ) + for options in tests: + with self.subTest(options=options): + with self.assertRaisesMessage(TypeError, msg): + management.call_command( + 'mutually_exclusive_required_with_same_dest', + **options, + ) + + def test_mutually_exclusive_group_required_with_same_dest_args(self): + tests = [ + ('--until=1',), + ('--until', 1), + ('--for=1',), + ('--for', 1), + ] + for args in tests: + out = StringIO() + with self.subTest(options=args): + management.call_command( + 'mutually_exclusive_required_with_same_dest', + *args, + stdout=out, + ) + output = out.getvalue() + self.assertIn('until=1', output) + def test_required_list_option(self): tests = [ (('--foo-list', [1, 2]), {}), |
