blob: a05a84122193ea5f1439e5cc15b947bebcb27d69 [file] [log] [blame]
# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements. See the NOTICE file distributed with
# this work for additional information regarding copyright ownership.
# The ASF licenses this file to You under the Apache License, Version 2.0
# (the "License"); you may not use this file except in compliance with
# the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""
Enhancements to Python's ``argparse`` module.
"""
from __future__ import absolute_import # so we can import standard 'argparse'
from argparse import ArgumentParser as BaseArgumentParser
class ArgumentParser(BaseArgumentParser):
"""
Enhanced argument parser.
Applied patch to fix `this issue <https://bugs.python.org/issue22433>`__.
"""
def add_flag_argument(self, name, help_true=None, help_false=None, default=False):
"""
Adds a flag argument as two arguments: ``--my-flag`` and ``--no-my-flag``.
"""
dest = name.replace('-', '_')
if default:
if help_true is not None:
help_true += ' (default)'
else:
help_true = '(default)'
else:
if help_false is not None:
help_false += ' (default)'
else:
help_false = '(default)'
group = self.add_mutually_exclusive_group()
group.add_argument('--%s' % name, action='store_true', help=help_true)
group.add_argument('--no-%s' % name, dest=dest, action='store_false', help=help_false)
self.set_defaults(**{dest: default})
def _parse_optional(self, arg_string):
if self._is_positional(arg_string):
return None
# if the option string is present in the parser, return the action
if arg_string in self._option_string_actions:
action = self._option_string_actions[arg_string]
return action, arg_string, None
# if the option string before the "=" is present, return the action
if '=' in arg_string:
option_string, explicit_arg = arg_string.split('=', 1)
if option_string in self._option_string_actions:
action = self._option_string_actions[option_string]
return action, option_string, explicit_arg
# search through all possible prefixes of the option string
# and all actions in the parser for possible interpretations
option_tuples = self._get_option_tuples(arg_string)
# if multiple actions match, the option string was ambiguous
if len(option_tuples) > 1:
options = ', '.join(
[option_string for action, option_string, explicit_arg in option_tuples])
tup = arg_string, options
self.error('ambiguous option: %s could match %s' % tup)
# if exactly one action matched, this segmentation is good,
# so return the parsed action
elif len(option_tuples) == 1:
option_tuple = option_tuples
return option_tuple
# if it was not found as an option, but it looks like a negative
# number, it was meant to be positional
# unless there are negative-number-like options
if self._negative_number_matcher.match(arg_string):
if not self._has_negative_number_optionals:
return None
# it was meant to be an optional but there is no such option
# in this parser (though it might be a valid option in a subparser)
return None, arg_string, None
def _is_positional(self, arg_string):
# if it's an empty string, it was meant to be a positional
if not arg_string:
return True
# if it doesn't start with a prefix, it was meant to be positional
if not arg_string[0] in self.prefix_chars:
return True
# if it's just a single character, it was meant to be positional
if len(arg_string) == 1:
return True
# if it contains a space, it was meant to be a positional
if ' ' in arg_string and arg_string[0] not in self.prefix_chars:
return True
return False