diff --git a/Lib/argparse.py b/Lib/argparse.py index dfc98695f64e0a..49579c8a986bf5 100644 --- a/Lib/argparse.py +++ b/Lib/argparse.py @@ -1981,12 +1981,12 @@ def _parse_known_args(self, arg_strings, namespace): def take_action(action, argument_strings, option_string=None): seen_actions.add(action) - argument_values = self._get_values(action, argument_strings) + argument_values, explicit = self._get_values(action, argument_strings) # error if this argument is not allowed with other previously # seen arguments, assuming that actions that use the default # value don't really count as "present" - if argument_values is not action.default: + if argument_values is not action.default or explicit: seen_non_default_actions.add(action) for conflict_action in action_conflicts.get(action, []): if conflict_action in seen_non_default_actions: @@ -2488,6 +2488,7 @@ def parse_known_intermixed_args(self, args=None, namespace=None): # ======================== def _get_values(self, action, arg_strings): # for everything but PARSER, REMAINDER args, strip out first '--' + explicit = True if action.nargs not in [PARSER, REMAINDER]: try: arg_strings.remove('--') @@ -2500,6 +2501,7 @@ def _get_values(self, action, arg_strings): value = action.const else: value = action.default + explicit = False if isinstance(value, str): value = self._get_value(action, value) self._check_value(action, value) @@ -2510,6 +2512,7 @@ def _get_values(self, action, arg_strings): not action.option_strings): if action.default is not None: value = action.default + explicit = False self._check_value(action, value) else: # since arg_strings is always [] at this point @@ -2542,7 +2545,7 @@ def _get_values(self, action, arg_strings): self._check_value(action, v) # return the converted value - return value + return value, explicit def _get_value(self, action, arg_string): type_func = self._registry_get('type', action.type, action.type) diff --git a/Lib/test/test_argparse.py b/Lib/test/test_argparse.py index 7c1f5d36999a3d..debbe2be155dad 100644 --- a/Lib/test/test_argparse.py +++ b/Lib/test/test_argparse.py @@ -3255,6 +3255,23 @@ def get_parser(self, required): test_successes_when_not_required = None test_successes_when_required = None +class TestMutuallyExclusiveRequiredDefault(TestCase): + + def test_default_arg_passed(self): + parser = ErrorRaisingArgumentParser() + group = parser.add_mutually_exclusive_group(required=True) + group.add_argument('--foo', default='1') + group.add_argument('--bar') + self.assertEqual(NS(foo='1', bar=None), + parser.parse_args(['--foo', '1'])) + + def test_nothing_passed(self): + parser = ErrorRaisingArgumentParser() + group = parser.add_mutually_exclusive_group(required=True) + group.add_argument('--foo', default='1') + group.add_argument('--bar') + self.assertRaises(ArgumentParserError, parser.parse_args, []) + # ================================================= # Mutually exclusive group in parent parser tests # ================================================= diff --git a/Misc/NEWS.d/next/Library/2021-02-16-18-13-00.bpo-43220.XfDZu0.rst b/Misc/NEWS.d/next/Library/2021-02-16-18-13-00.bpo-43220.XfDZu0.rst new file mode 100644 index 00000000000000..8d1863abfb68f9 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2021-02-16-18-13-00.bpo-43220.XfDZu0.rst @@ -0,0 +1 @@ +Accept explicitly passed default arguments in required argparse groups. Previously short strings that were used as default values in argparse groups would be rejected if they were passed with the same value. Now they are accepted. \ No newline at end of file