Disable/Remove argument in argparse
Solution 1
Despite the bug issue I mention below, your use of resolve
suggests a possible method. This is not for the novice or someone who needs to stick with the public API.
The parser
has a list of the Action (argument) objects (as created by add_argument
).
Using your 2nd parser definition, its _actions
list is:
In [22]: parser._actions
Out[22]:
[_HelpAction(option_strings=['-h', '--help'], dest='help'...),
_StoreAction(option_strings=['--arg2'], dest='arg2', nargs=None,
const=None, default=None, type=None, choices=None,
help='A second one', metavar=None),
_StoreAction(option_strings=['--arg1'], dest='arg1', nargs=None,
const=None, default=None, type=None, choices=None,
help='New number 1', metavar=None)]
When you add a conflicting one with resolve
, it removes the existing Action that conflicts. Look at the _handle_conflict_resolve
method for details. But I can fool it into removing an action without adding a new one.
In [23]: parser._handle_conflict_resolve(None, [('--arg1',parser._actions[2])])
Look at _actions
and help to verify that --arg1
is gone.
In [24]: parser._actions
Out[24]:
[_HelpAction(option_strings=['-h', '--help'], dest='help',....),
_StoreAction(option_strings=['--arg2'], dest='arg2', nargs=None,...)]
In [25]: parser.print_help()
usage: ipython3 [-h] [--arg2 ARG2]
optional arguments:
-h, --help show this help message and exit
--arg2 ARG2 A second one
resolve
just handles optionals
, ones where the flag strings might conflict. And it removes conflicting flags first, removing the conflicting action only if no flags remain. So be extra careful when you have both short and long options.
And this does not address the case of positionals. They don't have flags, and they may share dest
parameters. (though only one will appear in the result, unless they are appending actions).
In [27]: foo1 = parser.add_argument('foo',help='foo 1 positional')
In [28]: foo2 = parser.add_argument('foo',help='foo 2 positional')
In [29]: parser.print_help()
usage: ipython3 [-h] [--arg2 ARG2] foo foo
positional arguments:
foo foo 1 positional
foo foo 2 positional
...
Playing around a bit more, it looks like I can remove one of these new positionals:
In [33]: parser._actions[-1]
Out[33]: _StoreAction(option_strings=[], dest='foo',... help='foo 2 positional', metavar=None)
In [35]: foo2=parser._actions[-1]
In [36]: foo2.container._remove_action(foo2)
In [39]: parser.print_help()
usage: ipython3 [-h] [--arg2 ARG2] foo
positional arguments:
foo foo 1 positional
....
If I'd choosen _actions[-2]
I would have removed the first foo
. If I assign the value that add_argument
returns to a variable, e.g. foo1
, I can use that instead of looking up the value in the parser._actions
list. It may be helpful to run a sample parser in an interative shell (I use IPython) and look at these objects.
Again, this seems to work on a simple example, but it needs careful testing if used with something more complex (or for production).
The topic was raised on the Python bugs/issues a couple of years ago:
http://bugs.python.org/issue19462 Add remove_argument() method to argparse.ArgumentParser
I discussed the difficulties in complete removal, and suggested some alternatives. argparse.SUPPRESS
can be used to hide helps. optionals
can be ignored if they aren't required. positionals
are trickier, though I suggested tweaking their attributes (nargs
and default
). But it's been a while, so I need to review those posts.
=============================
I was curious about @2rs2ts
problem (see comment).
I made a parser, and then used it as parent to another parser (no need to use the subparser mechanism). Then I removed an argument from one parser, and looked at changes in the other parser.
Make a parent parser with one argument:
In [59]: p=argparse.ArgumentParser()
In [60]: p.add_argument('--foo')
Out[60]: _StoreAction(option_strings=['--foo'], dest='foo', nargs=None, const=None, default=None, type=None, choices=None, help=None, metavar=None)
Make another with parents
:
In [61]: p1=argparse.ArgumentParser(parents=[p],add_help=False)
In [62]: p1._actions
Out[62]:
[_HelpAction(option_strings=['-h', '--help'], dest='help', nargs=0, const=None, default='==SUPPRESS==', type=None, choices=None, help='show this help message and exit', metavar=None),
_StoreAction(option_strings=['--foo'], dest='foo', nargs=None, const=None, default=None, type=None, choices=None, help=None, metavar=None)]
Note that the 2nd Action is the same for both parsers (same id). parents
just copied a reference to the original --foo
Action, it did not make a copy.
In [63]: id(p._actions[1])
Out[63]: 3000108652
In [64]: id(p1._actions[1])
Out[64]: 3000108652
Now remove '--foo' from one parser, using the trick I worked out before:
In [65]: p1._handle_conflict_resolve(None,[('--foo',p1._actions[1])])
In [66]: p1._actions
Out[66]: [_HelpAction(option_strings=['-h', '--help'], dest='help', nargs=0, const=None, default='==SUPPRESS==', type=None, choices=None, help='show this help message and exit', metavar=None)]
'--foo' is gone from p1
list, but still present in the p
list. But option_strings
is now empty.
In [67]: p._actions
Out[67]:
[_HelpAction(option_strings=['-h', '--help'], dest='help', nargs=0, const=None, default='==SUPPRESS==', type=None, choices=None, help='show this help message and exit', metavar=None),
_StoreAction(option_strings=[], dest='foo', nargs=None, const=None, default=None, type=None, choices=None, help=None, metavar=None)]
The resolve
code removed conflicting option_strings
from the --foo
action, and then removed it from the p1._actions
list. But changing option_strings
for the p1
reference changed the p
reference as well.
argparse
uses a couple of ways to distinguish positionals
from optionals
, but the one used most often in parsing is to look at whether the option_strings
attribute is empty or not. By emptying this attribute, resolve
has effectively turned an optional
into a positional
.
Oops, my memory isn't what it should be.:) A year ago I answered a similar question involving parents
and resolve
https://stackoverflow.com/a/25821043/901925
argparse conflict resolver for options in subcommands turns keyword argument into positional argument
Solution 2
Is it possible to remove or disable an argument in argparse, such that it does not show in the help?
Set help
to argparse.SUPPRESS
when you add the argument, like this:
parser.add_argument('--arg1', help=argparse.SUPPRESS)
This will prevent the argument from showing up in the default help output.
Solution 3
Function to remove argparse options:
def remove_options(parser, options):
for option in options:
for action in parser._actions:
if vars(action)['option_strings'][0] == option:
parser._handle_conflict_resolve(None,[(option,action)])
break
Solution 4
Although hpaulj answer is great, in my case parser._remove_action(action)
was not removing positional arguments from the help. It also needed to be removed from the _action_group
:
def remove_argument(parser, arg):
for action in parser._actions:
opts = action.option_strings
if (opts and opts[0] == arg) or action.dest == arg:
parser._remove_action(action)
break
for action in parser._action_groups:
for group_action in action._group_actions:
if group_action.dest == arg:
action._group_actions.remove(group_action)
return
To remove --bar
, call:
remove_option(parser, "bar")
Related videos on Youtube
Bryan P
Updated on April 12, 2022Comments
-
Bryan P about 2 years
Is it possible to remove or disable an argument in argparse, such that it does not show in the help? How?
It is easy to add new arguments:
parser = argparse.ArgumentParser() parser.add_argument('--arg1', help='Argument 1') parser.add_argument('--arg2', help='A second one')
And I know you can override arguments with a new definition by specifying the "resolve" conflict handler:
#In one script that should stand-alone and include arg1: parser = argparse.ArgumentParser(conflict_handler='resolve') parser.add_argument('--arg1', help='Argument 1') parser.add_argument('--arg2', help='A second one') #In another script with similar options parser.add_argument('--arg1', help='New number 1')
But this still includes arg1 in the help message and results of
parse_args
Is there anything like#Wishful thinking #In another script with similar options, that shouldn't include arg1 parser.remove_argument('--arg1')
Or another reasonably easy way to achieve this?
Also: Would the approach be different if the argument was a positional argument?
Note: the problem with removing
arg1
after parsing as suggested here is that the argument still shows in the help-
jonrsharpe over 8 yearsWhy not factor out the shared parameters, then add each version of
arg1
in the script where it's actually needed? -
Bryan P over 8 years@jonrsharpe That may be the best option and is what I am doing now, but some co-developers are excited to keep a single command-line parsing function in the main, first, file so I wanted to look for alternatives.
-
-
Lukas Graf over 8 yearsThis will still parse the argument, and for example have it be required if it's flagged as such.
-
Bryan P over 8 yearsAlso, I would like the first file to run stand-alone and include the
arg1
help in the output display. I've edited the question to help clarify. -
Bryan P over 8 yearsOh, wait, I see now it seems I could use SUPPRESS in the second file. That should sort of work (although it does still treat the option as valid). But seems that parsing a positional argument might be problematic (added this consideration to original question.
-
Bryan P over 8 yearsGreat find. Seems argparse developers could use this machinery to make an official, supported implementation.
-
2rs2ts over 8 yearsThis wreaks havoc on subparsers. I have a base subparser and a bunch of other ones that mark that base as their
parent
. If I remove an optional argument from one subparser, then all the other subparsers still have the argument... but it becomes positional (but shows as optional in their help messages, still!) The same thing happens for Burhan's method too, so it's probably a limitation of the library. -
hpaulj over 8 yearsI added an explanation and a link to an earlier SO virtually the same problem.
-
Bryan P about 8 yearsInteresting. Will this both remove it from help and prevent the option from being parsed? Might you add a bit more explanation and an example?
-
stason almost 4 yearsThe code doesn't have a usage example. You have to pass an argument without leading dashes for it to work, e.g. to remove
--bar
, callremove_option(parser, "bar")
-
Eric Hansen about 2 yearsI love you. This works so well. Thank you @Lorenzo.