]> granicus.if.org Git - python/commitdiff
Issue #14910: Add allow_abbrev parameter to argparse.ArgumentParser.
authorBerker Peksag <berker.peksag@gmail.com>
Fri, 13 Feb 2015 23:39:17 +0000 (01:39 +0200)
committerBerker Peksag <berker.peksag@gmail.com>
Fri, 13 Feb 2015 23:39:17 +0000 (01:39 +0200)
Patch by Jonathan Paugh, Steven Bethard, paul j3 and Daniel Eriksson.

Doc/library/argparse.rst
Doc/whatsnew/3.5.rst
Lib/argparse.py
Lib/test/test_argparse.py
Misc/NEWS

index 8b3d9fc6a84986e7cd74f79ff3146ad40833b95d..1f75cd9e5f2ec034596af17581c6b92610a7655b 100644 (file)
@@ -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 <prefix-matching>` 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 <allow_abbrev>`
+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``
index 1de9e8fa5ea0ff4024f2d0b1195803da3b3706bc..c8be6943d4305c266445c5e01c790818e510bc64 100644 (file)
@@ -146,6 +146,14 @@ New Modules
 Improved Modules
 ================
 
+argparse
+--------
+
+* :class:`~argparse.ArgumentParser` now allows to disable
+  :ref:`abbreviated usage <prefix-matching>` of long options by setting
+  :ref:`allow_abbrev` to ``False``.
+  (Contributed by Jonathan Paugh, Steven Bethard, paul j3 and Daniel Eriksson.)
+
 cgi
 ---
 
index ba9e3df3806179880a3526fbcdf193dac1b9e7fe..9a067196dac3ffb6c413a1f144a5180fd2c6ccdc 100644 (file)
@@ -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
index a0b91628aca0586a873ee48faf039a5dc2c04438..d7f90cdaa98869a1a33d69e0d32afdca5f69a5b0 100644 (file)
@@ -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
 # ================
index cae74df68b7cbb272bdfb935d3df874e552cb76d..27b58e30451988af7bfcb160ad2dcef79494b537 100644 (file)
--- 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.