Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add remove_argument() method to argparse.ArgumentParser #63661

Closed
ustinov mannequin opened this issue Oct 31, 2013 · 16 comments
Closed

Add remove_argument() method to argparse.ArgumentParser #63661

ustinov mannequin opened this issue Oct 31, 2013 · 16 comments
Labels
stdlib Python modules in the Lib dir type-feature A feature request or enhancement

Comments

@ustinov
Copy link
Mannequin

ustinov mannequin commented Oct 31, 2013

BPO 19462
Nosy @warsaw, @rhettinger, @bitdancer, @ahirner

Note: these values reflect the state of the issue at the time it was migrated and might not reflect the current state.

Show more details

GitHub fields:

assignee = None
closed_at = <Date 2020-03-25.22:15:12.344>
created_at = <Date 2013-10-31.17:32:58.426>
labels = ['type-feature', 'library']
title = 'Add remove_argument() method to argparse.ArgumentParser'
updated_at = <Date 2020-03-25.22:15:12.343>
user = 'https://bugs.python.org/ustinov'

bugs.python.org fields:

activity = <Date 2020-03-25.22:15:12.343>
actor = 'rhettinger'
assignee = 'none'
closed = True
closed_date = <Date 2020-03-25.22:15:12.344>
closer = 'rhettinger'
components = ['Library (Lib)']
creation = <Date 2013-10-31.17:32:58.426>
creator = 'ustinov'
dependencies = []
files = []
hgrepos = []
issue_num = 19462
keywords = []
message_count = 15.0
messages = ['201832', '201836', '201838', '201839', '201882', '201890', '201913', '202763', '202764', '203499', '203511', '251739', '364892', '364895', '364903']
nosy_count = 7.0
nosy_names = ['barry', 'rhettinger', 'Arfrever', 'r.david.murray', 'paul.j3', 'ustinov', 'cybertreiber']
pr_nums = []
priority = 'normal'
resolution = 'out of date'
stage = 'resolved'
status = 'closed'
superseder = None
type = 'enhancement'
url = 'https://bugs.python.org/issue19462'
versions = ['Python 3.2']

@ustinov
Copy link
Mannequin Author

ustinov mannequin commented Oct 31, 2013

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

@ustinov ustinov mannequin added stdlib Python modules in the Lib dir type-feature A feature request or enhancement labels Oct 31, 2013
@bitdancer
Copy link
Member

Does conflict_handler='resolve' address your use case? It sounds like it should.

@ustinov
Copy link
Mannequin Author

ustinov mannequin commented Oct 31, 2013

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\>


@ustinov
Copy link
Mannequin Author

ustinov mannequin commented Oct 31, 2013

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\>


@paulj3
Copy link
Mannequin

paulj3 mannequin commented Nov 1, 2013

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.

@ustinov
Copy link
Mannequin Author

ustinov mannequin commented Nov 1, 2013

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

@paulj3
Copy link
Mannequin

paulj3 mannequin commented Nov 1, 2013

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()

@ustinov
Copy link
Mannequin Author

ustinov mannequin commented Nov 13, 2013

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?

@paulj3
Copy link
Mannequin

paulj3 mannequin commented Nov 13, 2013

argparse.SUPPRESS should do the trick. According to the documentation it can be used with default and help parameters.

@ustinov
Copy link
Mannequin Author

ustinov mannequin commented Nov 20, 2013

It does the trick with optionals but not the positionals.
How the positional arguments can be removed/hidden?

@paulj3
Copy link
Mannequin

paulj3 mannequin commented Nov 20, 2013

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.

@paulj3
Copy link
Mannequin

paulj3 mannequin commented Sep 28, 2015

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.

@ahirner
Copy link
Mannequin

ahirner mannequin commented Mar 23, 2020

@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)

@rhettinger
Copy link
Contributor

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?

@paulj3
Copy link
Mannequin

paulj3 mannequin commented Mar 23, 2020

I think it can be closed.

@ezio-melotti ezio-melotti transferred this issue from another repository Apr 10, 2022
@mixmastamyk
Copy link

mixmastamyk commented Apr 12, 2022

I just needed this function and used this implementation:
https://stackoverflow.com/a/49753634/450917

My use case is I have ~ten subcommands, nine of which need the same argument, one that doesn't. Easier to add it once to a common sub-parser, then remove it from the exception. Rather than repeat the add() nine times.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
stdlib Python modules in the Lib dir type-feature A feature request or enhancement
Projects
None yet
Development

No branches or pull requests

3 participants