summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--django/core/management/__init__.py6
-rw-r--r--tests/user_commands/management/commands/mutually_exclusive_required_with_same_dest.py13
-rw-r--r--tests/user_commands/tests.py35
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 60cfe86b3d..a727e56efb 100644
--- a/tests/user_commands/tests.py
+++ b/tests/user_commands/tests.py
@@ -275,6 +275,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]), {}),