diff -r 9ea84f006892 Lib/argparse.py --- a/Lib/argparse.py Wed May 01 15:15:50 2013 +0200 +++ b/Lib/argparse.py Mon Sep 09 15:27:36 2013 -0700 @@ -1801,12 +1801,15 @@ def take_action(action, argument_strings, option_string=None): seen_actions.add(action) - argument_values = self._get_values(action, argument_strings) + argument_values, using_default = 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: + # refine test so that only values set by _get_values() to + # action.default count as not-really-present + + if not using_default: seen_non_default_actions.add(action) for conflict_action in action_conflicts.get(action, []): if conflict_action in seen_non_default_actions: @@ -2212,6 +2215,7 @@ # ======================== def _get_values(self, action, arg_strings): # for everything but PARSER, REMAINDER args, strip out first '--' + using_default = False if action.nargs not in [PARSER, REMAINDER]: try: arg_strings.remove('--') @@ -2224,6 +2228,7 @@ value = action.const else: value = action.default + using_default = True if isinstance(value, str): value = self._get_value(action, value) self._check_value(action, value) @@ -2234,6 +2239,7 @@ not action.option_strings): if action.default is not None: value = action.default + using_default = True else: value = arg_strings self._check_value(action, value) @@ -2260,7 +2266,7 @@ self._check_value(action, v) # return the converted value - return value + return value, using_default def _get_value(self, action, arg_string): type_func = self._registry_get('type', action.type, action.type) diff -r 9ea84f006892 Lib/test/test_argparse.py --- a/Lib/test/test_argparse.py Wed May 01 15:15:50 2013 +0200 +++ b/Lib/test/test_argparse.py Mon Sep 09 15:27:36 2013 -0700 @@ -2689,6 +2689,52 @@ -c c help ''' + +class TestMutuallyExclusiveDefaults(MEMixin, TestCase): + '''Narrow seen_non_default_actions criteria: + _get_values() sets argument_values = action.default only for positionals + with empty strings and '?*' nargs. Optionals with a value that equals + (or 'is') the default don't count + ''' + def get_parser(self, required=None): + parser = ErrorRaisingArgumentParser(prog='PROG') + group = parser.add_mutually_exclusive_group(required=required) + group.add_argument('--foo', default='test') + group.add_argument('--bar', default='test') + group.add_argument('--small', type=int, default=42) + group.add_argument('--large', type=int, default=257) + return parser + + failures = ['--foo X --bar Y', + '--small 0 --large 34', + '--foo test --bar Y', + '--large 257 --foo X', + '--small 42 --foo X', + ] + successes = [ + ('--foo X', NS(bar='test', foo='X', large=257, small=42)), + ('--small 42', NS(bar='test', foo='test', large=257, small=42)), + ] + successes_when_not_required = [ + ('', NS(foo='test', bar='test', small=42, large=257)), + ] + usage_when_required = '''\ + usage: PROG [-h] (--foo FOO | --bar BAR | --small SMALL | --large LARGE) + ''' + usage_when_not_required = '''\ + usage: PROG [-h] [--foo FOO | --bar BAR | --small SMALL | --large LARGE] + ''' + help = '''\ + + optional arguments: + -h, --help show this help message and exit + --foo FOO + --bar BAR + --small SMALL + --large LARGE + ''' + + # ================================================= # Mutually exclusive group in parent parser tests # =================================================