# HG changeset patch # User Arnaud Fontaine # Date 1322193971 -32400 # Branch 2.7 # Node ID 3837457f1d3049492fe7ae5f2135e949cdbe85e4 # Parent d1bdafa161e7aed2e6fa070d3cca7e244f458409 #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 @@ -1705,10 +1705,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, basestring): - 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: @@ -1936,12 +1933,24 @@ if positionals: self.error(_('too few arguments')) - # make sure all required actions were present + # make sure all required actions were present and also convert + # action defaults which were not given as arguments for action in self._actions: - if action.required: - if action not in seen_actions: + if action not in seen_actions: + if action.required: name = _get_action_name(action) self.error(_('argument %s is required') % name) + else: + # If default specified (None means it was not), 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 is getattr(namespace, action.dest)): + setattr(namespace, action.dest, + self._get_value(action, action.default)) # make sure all required groups had one option present for group in self._mutually_exclusive_groups: 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 @@ -4416,6 +4416,38 @@ else: self.fail() +# ================================================ +# 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 @@ -935,3 +935,4 @@ Uwe Zessin Tarek Ziadé Peter Åstrand +Arnaud Fontaine diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -79,6 +79,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.