From 8089cd642fa8b29e852506218b6355be064c2bd5 Mon Sep 17 00:00:00 2001 From: Berker Peksag Date: Sat, 14 Feb 2015 01:39:17 +0200 Subject: Issue #14910: Add allow_abbrev parameter to argparse.ArgumentParser. Patch by Jonathan Paugh, Steven Bethard, paul j3 and Daniel Eriksson. --- Doc/library/argparse.rst | 35 +++++++++++++++++++++++++++++++---- Doc/whatsnew/3.5.rst | 8 ++++++++ Lib/argparse.py | 40 ++++++++++++++++++++++------------------ Lib/test/test_argparse.py | 33 +++++++++++++++++++++++++++++++++ Misc/NEWS | 3 +++ 5 files changed, 97 insertions(+), 22 deletions(-) diff --git a/Doc/library/argparse.rst b/Doc/library/argparse.rst index 8b3d9fc..1f75cd9 100644 --- a/Doc/library/argparse.rst +++ b/Doc/library/argparse.rst @@ -135,7 +135,7 @@ ArgumentParser objects formatter_class=argparse.HelpFormatter, \ prefix_chars='-', fromfile_prefix_chars=None, \ argument_default=None, conflict_handler='error', \ - add_help=True) + add_help=True, allow_abbrev=True) Create a new :class:`ArgumentParser` object. All parameters should be passed as keyword arguments. Each parameter has its own more detailed description @@ -169,6 +169,12 @@ ArgumentParser objects * add_help_ - Add a -h/--help option to the parser (default: ``True``) + * allow_abbrev_ - Allows long options to be abbreviated if the + abbreviation is unambiguous. (default: ``True``) + + .. versionchanged:: 3.5 + *allow_abbrev* parameter was added. + The following sections describe how each of these are used. @@ -518,6 +524,26 @@ calls, we supply ``argument_default=SUPPRESS``:: >>> parser.parse_args([]) Namespace() +.. _allow_abbrev: + +allow_abbrev +^^^^^^^^^^^^ + +Normally, when you pass an argument list to the +:meth:`~ArgumentParser.parse_args` method of a :class:`ArgumentParser`, +it :ref:`recognizes abbreviations ` of long options. + +This feature can be disabled by setting ``allow_abbrev`` to ``False``:: + + >>> parser = argparse.ArgumentParser(prog='PROG', allow_abbrev=False) + >>> parser.add_argument('--foobar', action='store_true') + >>> parser.add_argument('--foonley', action='store_false') + >>> parser.parse_args([--foon]) + usage: PROG [-h] [--foobar] [--foonley] + PROG: error: unrecognized arguments: --foon + +.. versionadded:: 3.5 + conflict_handler ^^^^^^^^^^^^^^^^ @@ -1410,9 +1436,9 @@ argument:: Argument abbreviations (prefix matching) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -The :meth:`~ArgumentParser.parse_args` method allows long options to be -abbreviated to a prefix, if the abbreviation is unambiguous (the prefix matches -a unique option):: +The :meth:`~ArgumentParser.parse_args` method :ref:`by default ` +allows long options to be abbreviated to a prefix, if the abbreviation is +unambiguous (the prefix matches a unique option):: >>> parser = argparse.ArgumentParser(prog='PROG') >>> parser.add_argument('-bacon') @@ -1426,6 +1452,7 @@ a unique option):: PROG: error: ambiguous option: -ba could match -badger, -bacon An error is produced for arguments that could produce more than one options. +This feature can be disabled by setting :ref:`allow_abbrev` to ``False``. Beyond ``sys.argv`` diff --git a/Doc/whatsnew/3.5.rst b/Doc/whatsnew/3.5.rst index 1de9e8f..c8be694 100644 --- a/Doc/whatsnew/3.5.rst +++ b/Doc/whatsnew/3.5.rst @@ -146,6 +146,14 @@ New Modules Improved Modules ================ +argparse +-------- + +* :class:`~argparse.ArgumentParser` now allows to disable + :ref:`abbreviated usage ` of long options by setting + :ref:`allow_abbrev` to ``False``. + (Contributed by Jonathan Paugh, Steven Bethard, paul j3 and Daniel Eriksson.) + cgi --- diff --git a/Lib/argparse.py b/Lib/argparse.py index ba9e3df..9a06719 100644 --- a/Lib/argparse.py +++ b/Lib/argparse.py @@ -1590,6 +1590,7 @@ class ArgumentParser(_AttributeHolder, _ActionsContainer): - argument_default -- The default value for all arguments - conflict_handler -- String indicating how to handle conflicts - add_help -- Add a -h/-help option + - allow_abbrev -- Allow long options to be abbreviated unambiguously """ def __init__(self, @@ -1603,7 +1604,8 @@ class ArgumentParser(_AttributeHolder, _ActionsContainer): fromfile_prefix_chars=None, argument_default=None, conflict_handler='error', - add_help=True): + add_help=True, + allow_abbrev=True): superinit = super(ArgumentParser, self).__init__ superinit(description=description, @@ -1621,6 +1623,7 @@ class ArgumentParser(_AttributeHolder, _ActionsContainer): self.formatter_class = formatter_class self.fromfile_prefix_chars = fromfile_prefix_chars self.add_help = add_help + self.allow_abbrev = allow_abbrev add_group = self.add_argument_group self._positionals = add_group(_('positional arguments')) @@ -2098,23 +2101,24 @@ class ArgumentParser(_AttributeHolder, _ActionsContainer): 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]) - args = {'option': arg_string, 'matches': options} - msg = _('ambiguous option: %(option)s could match %(matches)s') - self.error(msg % args) - - # 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 self.allow_abbrev: + # 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]) + args = {'option': arg_string, 'matches': options} + msg = _('ambiguous option: %(option)s could match %(matches)s') + self.error(msg % args) + + # 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 diff --git a/Lib/test/test_argparse.py b/Lib/test/test_argparse.py index a0b9162..d7f90cd 100644 --- a/Lib/test/test_argparse.py +++ b/Lib/test/test_argparse.py @@ -753,6 +753,39 @@ class TestOptionalsActionCount(ParserTestCase): ] +class TestOptionalsAllowLongAbbreviation(ParserTestCase): + """Allow long options to be abbreviated unambiguously""" + + argument_signatures = [ + Sig('--foo'), + Sig('--foobaz'), + Sig('--fooble', action='store_true'), + ] + failures = ['--foob 5', '--foob'] + successes = [ + ('', NS(foo=None, foobaz=None, fooble=False)), + ('--foo 7', NS(foo='7', foobaz=None, fooble=False)), + ('--fooba a', NS(foo=None, foobaz='a', fooble=False)), + ('--foobl --foo g', NS(foo='g', foobaz=None, fooble=True)), + ] + + +class TestOptionalsDisallowLongAbbreviation(ParserTestCase): + """Do not allow abbreviations of long options at all""" + + parser_signature = Sig(allow_abbrev=False) + argument_signatures = [ + Sig('--foo'), + Sig('--foodle', action='store_true'), + Sig('--foonly'), + ] + failures = ['-foon 3', '--foon 3', '--food', '--food --foo 2'] + successes = [ + ('', NS(foo=None, foodle=False, foonly=None)), + ('--foo 3', NS(foo='3', foodle=False, foonly=None)), + ('--foonly 7 --foodle --foo 2', NS(foo='2', foodle=True, foonly='7')), + ] + # ================ # Positional tests # ================ diff --git a/Misc/NEWS b/Misc/NEWS index cae74df..27b58e3 100644 --- a/Misc/NEWS +++ b/Misc/NEWS @@ -13,6 +13,9 @@ Core and Builtins Library ------- +- Issue #14910: Add allow_abbrev parameter to argparse.ArgumentParser. Patch by + Jonathan Paugh, Steven Bethard, paul j3 and Daniel Eriksson. + - Issue #21717: tarfile.open() now supports 'x' (exclusive creation) mode. - Issue #23344: marshal.dumps() is now 20-25% faster on average. -- cgit v0.12