1 # Licensed to the Apache Software Foundation (ASF) under one or more
2 # contributor license agreements. See the NOTICE file distributed with
3 # this work for additional information regarding copyright ownership.
4 # The ASF licenses this file to You under the Apache License, Version 2.0
5 # (the "License"); you may not use this file except in compliance with
6 # the License. You may obtain a copy of the License at
8 # http://www.apache.org/licenses/LICENSE-2.0
10 # Unless required by applicable law or agreed to in writing, software
11 # distributed under the License is distributed on an "AS IS" BASIS,
12 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 # See the License for the specific language governing permissions and
14 # limitations under the License.
17 Enhancements to Python's ``argparse`` module.
20 from __future__ import absolute_import # so we can import standard 'argparse'
22 from argparse import ArgumentParser as BaseArgumentParser
25 class ArgumentParser(BaseArgumentParser):
27 Enhanced argument parser.
29 Applied patch to fix `this issue <https://bugs.python.org/issue22433>`__.
32 def add_flag_argument(self, name, help_true=None, help_false=None, default=False):
34 Adds a flag argument as two arguments: ``--my-flag`` and ``--no-my-flag``.
37 dest = name.replace('-', '_')
40 if help_true is not None:
41 help_true += ' (default)'
43 help_true = '(default)'
45 if help_false is not None:
46 help_false += ' (default)'
48 help_false = '(default)'
50 group = self.add_mutually_exclusive_group()
51 group.add_argument('--%s' % name, action='store_true', help=help_true)
52 group.add_argument('--no-%s' % name, dest=dest, action='store_false', help=help_false)
54 self.set_defaults(**{dest: default})
56 def _parse_optional(self, arg_string):
58 if self._is_positional(arg_string):
61 # if the option string is present in the parser, return the action
62 if arg_string in self._option_string_actions:
63 action = self._option_string_actions[arg_string]
64 return action, arg_string, None
66 # if the option string before the "=" is present, return the action
68 option_string, explicit_arg = arg_string.split('=', 1)
69 if option_string in self._option_string_actions:
70 action = self._option_string_actions[option_string]
71 return action, option_string, explicit_arg
73 # search through all possible prefixes of the option string
74 # and all actions in the parser for possible interpretations
75 option_tuples = self._get_option_tuples(arg_string)
77 # if multiple actions match, the option string was ambiguous
78 if len(option_tuples) > 1:
80 [option_string for action, option_string, explicit_arg in option_tuples])
81 tup = arg_string, options
82 self.error('ambiguous option: %s could match %s' % tup)
84 # if exactly one action matched, this segmentation is good,
85 # so return the parsed action
86 elif len(option_tuples) == 1:
87 option_tuple = option_tuples
90 # if it was not found as an option, but it looks like a negative
91 # number, it was meant to be positional
92 # unless there are negative-number-like options
93 if self._negative_number_matcher.match(arg_string):
94 if not self._has_negative_number_optionals:
97 # it was meant to be an optional but there is no such option
98 # in this parser (though it might be a valid option in a subparser)
99 return None, arg_string, None
101 def _is_positional(self, arg_string):
102 # if it's an empty string, it was meant to be a positional
106 # if it doesn't start with a prefix, it was meant to be positional
107 if not arg_string[0] in self.prefix_chars:
110 # if it's just a single character, it was meant to be positional
111 if len(arg_string) == 1:
114 # if it contains a space, it was meant to be a positional
115 if ' ' in arg_string and arg_string[0] not in self.prefix_chars: