classification
Title: argparse doesn't recognise two option aliases as equal
Type: behavior Stage: resolved
Components: Library (Lib) Versions: Python 3.5
process
Status: closed Resolution: not a bug
Dependencies: Superseder:
Assigned To: Nosy List: Krzysztof Leszczyński, josh.r, paul.j3
Priority: normal Keywords:

Created on 2018-02-12 15:07 by Krzysztof Leszczyński, last changed 2018-09-30 03:40 by paul.j3. This issue is now closed.

Messages (5)
msg312058 - (view) Author: Krzysztof Leszczyński (Krzysztof Leszczyński) Date: 2018-02-12 15:09
Step to reproduce:

import argparse
ap=argparse.ArgumentParser()
ap.add_argument("--a-b", "--ab")
v1=ap.parse_args(["--ab", "xx"])
print(v1)
# v1==Namespace(a_b='xx')
v2=ap.parse_args(["--a", "xx"])
# v2 should be equal to v1 but instead it raises an error, in spite that --a-b and --ab are aliases
msg312091 - (view) Author: Josh Rosenberg (josh.r) * (Python triager) Date: 2018-02-13 01:32
To be clear, the specific error is:

error: ambiguous option: --a could match --a-b, --ab

which makes sense if they're separate switches, but doesn't make sense when both expansions are aliases of one another.
msg312097 - (view) Author: paul j3 (paul.j3) * (Python triager) Date: 2018-02-13 03:01
Subparsers have aliases, argument option strings don't, at least not formally.  Once an argument is added, its flags are entered in several lists.  One list belongs to the Action itself, another belongs to the parser (it might actually be a dictionary).  The code could check that '--a-b' and '--ab' belong to the same Action, but I suspect it doesn't, at least not at the point where it is checking for ambiguity.

We can look at the code to verify this.  It is also entirely likely that this issue has already been raised.  There are other issues regarding how it checks for abbreviations (and how to disable abbreviations).
msg312099 - (view) Author: paul j3 (paul.j3) * (Python triager) Date: 2018-02-13 03:37
When I run your setup in ipython, I see a display of the newly added Action:

   Out[2]: _StoreAction(option_strings=['--a-b', '--ab'], dest='a_b', 
   nargs=None, const=None, default=None, type=None, choices=None, help=None, 
   metavar=None)

Note the 'option_strings' list.

This strings are also entered as keys in a parser dictionary:

    In [6]: list(ap._option_string_actions.keys())
    Out[6]: ['--a-b', '--help', '--ab', '-h']

The values are the corresponding Actions, in this case the default 'help' one, and the newly added 'StoreAction'.  So the parser can only tell if two keys are 'aliases' by checking for matching values.

The abbreviation ambiguity error is raised in 'parser._parse_optional'.  If 'ap.allow_abbrev' is does

    ap._get_option_tuples('--a')

and raises the error if this returns more than one Action.  It does not check whether the multiple actions has the same ID.  I suppose it could, but it doesn't.

The option string is passed to the Action.__call__:

    def __call__(self, parser, namespace, values, option_string=None):
        setattr(namespace, self.dest, values)

None of the defined Action subclasses makes use of the this 'option_string' parameter (that I recall).  But I can easily imagine writing a custom Action class that does make use of this parameter.  

Are aliases like this needed?  Seems they just clutter the help:

    usage: ipython3 [-h] [--a-b A_B]

    optional arguments:
        -h, --help           show this help message and exit
        --a-b A_B, --ab A_B

The clutter will be more obvious with longer realistic flags.

Here the aliases end up disabling the '--a' abbreviation.  '--a-' still works.
msg326699 - (view) Author: paul j3 (paul.j3) * (Python triager) Date: 2018-09-30 03:40
I'm going to close this issue.  The current behavior is a logical consequence of how option_strings and abbreviations are handled.  Handling this particular case differently would be require adding a special test, as opposed to a minor tweak to the current code.

It could be reopened if some one wrote a clever and complete patch (and/or made a compelling case that it is needed).
History
Date User Action Args
2018-09-30 03:40:20paul.j3setstatus: open -> closed
resolution: not a bug
messages: + msg326699

stage: resolved
2018-02-13 03:37:14paul.j3setmessages: + msg312099
2018-02-13 03:01:36paul.j3setmessages: + msg312097
2018-02-13 01:32:14josh.rsetnosy: + josh.r
messages: + msg312091
2018-02-12 22:02:47paul.j3setnosy: + paul.j3
2018-02-12 15:09:55Krzysztof Leszczyńskisetmessages: + msg312058
2018-02-12 15:07:34Krzysztof Leszczyńskicreate