classification
Title: Add remove_argument() method to argparse.ArgumentParser
Type: enhancement Stage: resolved
Components: Library (Lib) Versions: Python 3.2
process
Status: closed Resolution: out of date
Dependencies: Superseder:
Assigned To: Nosy List: Arfrever, barry, cybertreiber, paul.j3, r.david.murray, rhettinger, ustinov
Priority: normal Keywords:

Created on 2013-10-31 17:32 by ustinov, last changed 2020-03-25 22:15 by rhettinger. This issue is now closed.

Messages (15)
msg201832 - (view) Author: Artem Ustinov (ustinov) Date: 2013-10-31 17:32
In order to migrate from optparse to argparse we need to have an ability to substitute anguments, e.g. remove and then create.

In our framework we use the command line utility base class and then inherit the particular tools from it. The parser in base class uses add_argument() to populate the general argument list but for some tools it is needed to modify the inherited arguments set and make some arguments to have the modified meaning.

With optparse we've just used remove_option() and then added the modified one with add_option() but argparse currently does not have this functionality.

For the purpose above we just need to have remove_argument() or modify_argument() methods in orgparse
msg201836 - (view) Author: R. David Murray (r.david.murray) * (Python committer) Date: 2013-10-31 17:54
Does conflict_handler='resolve' address your use case?  It sounds like it should.
msg201838 - (view) Author: Artem Ustinov (ustinov) Date: 2013-10-31 18:11
We need argparse to raise an error for conflicting options and that's why
we need to implicitly substitute an option when we need it
On 31 Oct 2013 19:54, "R. David Murray" <report@bugs.python.org> wrote:

>
> R. David Murray added the comment:
>
> Does conflict_handler='resolve' address your use case?  It sounds like it
> should.
>
> ----------
> nosy: +r.david.murray
>
> _______________________________________
> Python tracker <report@bugs.python.org>
> <http://bugs.python.org/issue19462>
> _______________________________________
>
msg201839 - (view) Author: Artem Ustinov (ustinov) Date: 2013-10-31 18:13
Explicitly substitute, excuse me
On 31 Oct 2013 20:11, "Artem Ustinov" <report@bugs.python.org> wrote:

>
> Artem Ustinov added the comment:
>
> We need argparse to raise an error for conflicting options and that's why
> we need to implicitly substitute an option when we need it
> On 31 Oct 2013 19:54, "R. David Murray" <report@bugs.python.org> wrote:
>
> >
> > R. David Murray added the comment:
> >
> > Does conflict_handler='resolve' address your use case?  It sounds like it
> > should.
> >
> > ----------
> > nosy: +r.david.murray
> >
> > _______________________________________
> > Python tracker <report@bugs.python.org>
> > <http://bugs.python.org/issue19462>
> > _______________________________________
> >
>
> ----------
>
> _______________________________________
> Python tracker <report@bugs.python.org>
> <http://bugs.python.org/issue19462>
> _______________________________________
>
msg201882 - (view) Author: paul j3 (paul.j3) * (Python triager) Date: 2013-11-01 07:35
When you add an argument, argparse creates an `Action`, and returns it.  It also places that action in various lists (e.g. parse._actions) and dictionaries.  A `remove_argument` function would have to trace and remove all of those links.  That's a non-trivial task.  However modifying an argument (or Action) is much easier, since there is only one instance.  Obviously some modifications will be safer than others.

For example:

    parser = ArgumentParser()
    a = parser.add_argument('--foo')
    print a

produces:

    _StoreAction(option_strings=['--foo'], dest='foo', nargs=None,    const=None, default=None, type=None, choices=None, help=None, metavar=None)

`vars(a)` gives a somewhat longer list of attributes.  Within reason, those attributes can be changed directly.  I think the 'dest', 'help', 'nargs', 'metavar' could all be changed without hidden effects.  There's also a 'required' attribute that could be changed for optionals.  Changing the 'option_strings' might be problematic, since the parser has a dictionary using those strings as keys.

The constant `argparse.SUPPRESS` is used in several places to alter actions.  For example, to suppress the help, or to suppress default values in the Namespace.  So it might be possible to 'hide' arguments in the subclass, even if you can't remove them.

In http://bugs.python.org/issue14191 I explored a couple of ways of temporarily 'deactivating' certain groups of arguments, so as to parse the optionals and positionals separately.  It's an advance issue, but might still give some ideas. 

Another possibility is to use 'parent' parsers to define clusters of arguments.  Your base class could create a parser with one set of parents, and the subclass could use a different set.
msg201890 - (view) Author: Artem Ustinov (ustinov) Date: 2013-11-01 11:31
Paul,
essentialy, what i looking for is to replace the 'help' string of the
inherited argument with the new one. If you say it could be changed without
any effect so what would be the proper way to do it using argparse?
Artem
msg201913 - (view) Author: paul j3 (paul.j3) * (Python triager) Date: 2013-11-01 16:29
Just hang on the Action object that the `add_argument` returned, and change its `help` attribute.

    a = parser.add_argument('--foo', help='initial help')
    ....
    a.help = 'new help'

If using a custom parser class and subclass, I'd do something like:

    self.changeablearg = self.parser.add_argument...

in the class, and

    self.changeablearg.help = 'new help'

in the subclass

You can test the help message with

    print parser.format_help()
msg202763 - (view) Author: Artem Ustinov (ustinov) Date: 2013-11-13 17:21
What is the way to 'hide' the argument from being parsed?

E.g. we have self.parser.add_argument('foo') in parent class,
how can we modify it in child class so that it would not to
appear in --help strings and not populated to child's Namespace?
msg202764 - (view) Author: paul j3 (paul.j3) * (Python triager) Date: 2013-11-13 17:31
`argparse.SUPPRESS` should do the trick.  According to the documentation it can be used with `default` and `help` parameters.
msg203499 - (view) Author: Artem Ustinov (ustinov) Date: 2013-11-20 16:24
It does the trick with optionals but not the positionals.
How the positional arguments can be removed/hidden?
msg203511 - (view) Author: paul j3 (paul.j3) * (Python triager) Date: 2013-11-20 17:14
f.nargs = '?'
    f.default = argparse.SUPPRESS
    f.help = argparse.SUPPRESS

may be best set of tweaks to a positional Action `f`.  In quick tests it removes `f` from the help, suppresses any complaints about a missing string, and does not put anything in the namespace.

But if there is a string in the input that could match this positional, it will be use.

    f.nargs = 0

is another option.  This puts a `[]` (empty list) in the namespace, since 'nothing' matches `f`.  If there is an input string that might have matched it before, you will not get an 'unrecognized argument' error.  `parse_known_args` can be used to get around that issue.

I should stress, though, that fiddling with `nargs` like this is not part of the API.  Tweak this at your own risk.
msg251739 - (view) Author: paul j3 (paul.j3) * (Python triager) Date: 2015-09-28 01:13
I realized while answering a Stackoverflow question that the resolve method has the pieces for removing an Action from the parser.  It removes an existing Action so that the new, conflicting one, can replace it.

It's meant to be used with flagged arguments.  If `arg1` is an action with one option_string, it can be removed with:

    parser._handle_conflict_resolve(None, [('--arg1', arg1)])

If it is a positional, this call seems to be sufficient:

    arg1.container._remove_action(arg1)

http://stackoverflow.com/a/32809642/901925

Beyond answering this question I haven't tested the idea.  If there's more interest I could explore it more.
msg364892 - (view) Author: Alexander Hirner (cybertreiber) Date: 2020-03-23 20:01
@paul j3
FWIW, popping optionals from a cache that is maintained in addition to self._actions, makes special conflict handling unnecessary.

i.e.:
        for option_string in a.option_strings:
            parser._option_string_actions.pop(option_string)
msg364895 - (view) Author: Raymond Hettinger (rhettinger) * (Python committer) Date: 2020-03-23 20:11
> In order to migrate from optparse to argparse we need to have 
> an ability to substitute anguments, e.g. remove and then create.

It seems to me that the original use case is obsolete.

Paul, do you think this issue should be closed?
msg364903 - (view) Author: paul j3 (paul.j3) * (Python triager) Date: 2020-03-23 21:39
I think it can be closed.
History
Date User Action Args
2020-03-25 22:15:12rhettingersetstatus: open -> closed
resolution: out of date
stage: resolved
2020-03-23 21:39:14paul.j3setmessages: + msg364903
2020-03-23 20:11:14rhettingersetnosy: + rhettinger
messages: + msg364895
2020-03-23 20:01:42cybertreibersetnosy: + cybertreiber
messages: + msg364892
2015-09-28 01:14:00paul.j3setmessages: + msg251739
2013-11-20 17:14:01paul.j3setmessages: + msg203511
2013-11-20 16:24:12ustinovsetmessages: + msg203499
2013-11-13 17:31:21paul.j3setmessages: + msg202764
2013-11-13 17:21:46ustinovsetmessages: + msg202763
2013-11-01 16:29:38paul.j3setmessages: + msg201913
2013-11-01 11:31:27ustinovsetmessages: + msg201890
2013-11-01 07:36:00paul.j3setnosy: + paul.j3
messages: + msg201882
2013-11-01 00:41:44Arfreversetnosy: + Arfrever
2013-10-31 19:01:38barrysetnosy: + barry
2013-10-31 18:13:59ustinovsetmessages: + msg201839
2013-10-31 18:11:56ustinovsetmessages: + msg201838
2013-10-31 17:54:02r.david.murraysetnosy: + r.david.murray
messages: + msg201836
2013-10-31 17:32:58ustinovcreate