python / cpython

The Python programming language
https://www.python.org
Other
61.96k stars 29.79k forks source link

argparse with option/value like --gpg-options "--homedir=/home/user" errors out but adding binding op works. #118551

Open kwloafman opened 3 months ago

kwloafman commented 3 months ago

Bug report

Bug description:

--gpg-options is defined as follows:

    gpg_options=dict(
        metavar=_("options"),
        action=SplitOptionsAction,
        help="Verbatim gpg options.  May be supplied multiple times.",
        default=dflt(config.gpg_options),
    ),

This does not work (using space):

prog --gpg-options "--homedir=/home/user"

and gets argument --gpg-options: expected one argument

But this one does (using the binding op):

prog --gpg-options="--homedir=/home/user"

with gpg_options being set correctly.

The deprecated optparse handled this correctly.

See Also: https://gitlab.com/duplicity/duplicity/-/issues/795 https://gitlab.com/duplicity/duplicity/-/issues/816

CPython versions tested on:

3.8, 3.9, 3.10, 3.11, 3.12

Operating systems tested on:

Linux, macOS

vadmium commented 3 months ago

You might like to add a clear way to produce the bug. How does the _gpgoptions dictionary interface with the argparse module?

So far this sounds like the known bug #53580. Unfortunately, the documentation https://docs.python.org/3/library/argparse.html#arguments-containing indicates it is intended that cases like this assume the user made a mistake and an error is reported.

kwloafman commented 3 months ago

It's a simple case of using a dataclass of dictionaries so that we can do:

    for opt in sorted(all_options):
        var = opt2var(opt)
        names = [opt] + OptionAliases.__dict__.get(var, [])
        parser.add_argument(*names, **OptionKwargs[var])

where OptionKwargs[var] is the dictionary above, one of 109 in duplicity.

So putting it all together it would appear as if expanded to multiple calls:


    parser.add_argument(
        "gpg-options", 
        metavar=_("options"),
        action=SplitOptionsAction,
        help="Verbatim gpg options.  May be supplied multiple times.",
        default=dflt(config.gpg_options),
)
eli-schwartz commented 3 months ago

Note that per the linked ticket, the overall answer here is that optparse has POSIX-compatible semantics and argparse does not, and if you find yourself hitting this argparse bug the answer is to switch (back?) to optparse.

kwloafman commented 3 months ago

When exactly does optparse get removed? It's been deprecated for years.

eli-schwartz commented 3 months ago

There are no plans to ever remove it.

CPython considers it feature- complete and closed for future development, and also considered less ergonomic than argparse, hence "soft deprecated in the sense that for existing scripts it's fine to keep using it forever".

Note that argparse is also considered feature-complete and no one is really willing to develop it further beyond bugfixes, because it turns out that argument parsing libraries are hard and no one wants to touch them or something. 🤷 Ask most people and they will tell you to give up on the standard library as a bad idea and go use click instead.