# HG changeset patch # User Arnaud Fontaine # Date 1322195153 -32400 # Node ID f33675f073b5b8c2df94e044f00980a998992aca # Parent 736b0aec412b3fbdcaf2676f0d823425fe77795b #12776: call argparse type function (specified by add_argument) only once. diff --git a/Lib/argparse.py b/Lib/argparse.py --- a/Lib/argparse.py +++ b/Lib/argparse.py @@ -1739,10 +1739,7 @@ if action.dest is not SUPPRESS: if not hasattr(namespace, action.dest): if action.default is not SUPPRESS: - default = action.default - if isinstance(action.default, str): - default = self._get_value(action, default) - setattr(namespace, action.dest, default) + setattr(namespace, action.dest, action.default) # add any parser defaults that aren't present for dest in self._defaults: @@ -1968,6 +1965,25 @@ # make sure all required actions were present required_actions = [_get_action_name(action) for action in self._actions if action.required and action not in seen_actions] + + # make sure all required actions were present and also convert + # action defaults which were not given as arguments + required_actions = [] + for action in self._actions: + if action not in seen_actions: + if action.required: + required_actions.append(_get_action_name(action)) + else: + # Convert action default now instead of doing it before + # parsing arguments to avoid calling convert functions + # twice (which may fail) if the argument was given, but + # only if it was defined already in the namespace + if (action.default is not None and + hasattr(namespace, action.dest) and + action.default == getattr(namespace, action.dest)): + setattr(namespace, action.dest, + self._get_value(action, action.default)) + if required_actions: self.error(_('the following arguments are required: %s') % ', '.join(required_actions)) diff --git a/Lib/test/test_argparse.py b/Lib/test/test_argparse.py --- a/Lib/test/test_argparse.py +++ b/Lib/test/test_argparse.py @@ -4543,6 +4543,38 @@ self.assertNotIn(msg, 'optional_positional') +# ================================================ +# Check that the type function is called only once +# ================================================ + +class TestTypeFunctionCallOnlyOnce(TestCase): + + def test_type_function_call_only_once(self): + def spam(string_to_convert): + self.assertEqual(string_to_convert, 'spam!') + return 'foo_converted' + + parser = argparse.ArgumentParser() + parser.add_argument('--foo', type=spam, default='bar') + args = parser.parse_args('--foo spam!'.split()) + self.assertEqual(NS(foo='foo_converted'), args) + +# ================================================================ +# Check that the type function is called with a non-string default +# ================================================================ + +class TestTypeFunctionCallWithNonStringDefault(TestCase): + + def test_type_function_call_with_non_string_default(self): + def spam(int_to_convert): + self.assertEqual(int_to_convert, 0) + return 'foo_converted' + + parser = argparse.ArgumentParser() + parser.add_argument('--foo', type=spam, default=0) + args = parser.parse_args([]) + self.assertEqual(NS(foo='foo_converted'), args) + # ====================== # parse_known_args tests # ====================== diff --git a/Misc/ACKS b/Misc/ACKS --- a/Misc/ACKS +++ b/Misc/ACKS @@ -1089,3 +1089,4 @@ Kai Zhu Tarek Ziadé Peter Åstrand +Arnaud Fontaine diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -390,6 +390,10 @@ Library ------- +- Issue #12776: call argparse type function (specified by add_argument) + only once. Before, type function was called twice in case the default + was specified and the argument was given as well. + - Issue #13458: Fix a memory leak in the ssl module when decoding a certificate with a subjectAltName. Patch by Robert Xiao.