python's argument parser printing the argument name in upper case - python

I am trying to write usage/help for my python script using the argparse library.
This is my sample code:
import argparse
parser = argparse.ArgumentParser(
description='My description')
parser.add_argument(
"-r", "--remote",
help="help message")
parser.print_help()
Output:
usage: [-h] [-r REMOTE]
My description
optional arguments:
-h, --help show this help message and exit
-r REMOTE, --remote REMOTE
help message
I have no idea why it is printing REMOTE after the -r and --remote switches in the above output.
Can anyone tell me what am I doing wrong here or what should I do to get rid of it?

You are looking at the metavar; it is autogenerated from the option string to form a placeholder. It tells the user that it is there that they need to fill in a value.
You can set an explicit metavar value with the metavar keyword argument:
When ArgumentParser generates help messages, it needs some way to refer to each expected argument. By default, ArgumentParser objects use the dest value as the “name” of each object. By default, for positional argument actions, the dest value is used directly, and for optional argument actions, the dest value is uppercased.
You see it because your argument takes a value; if you expected it to be a toggle, use action='store_true'; in that case the option defaults to False unless the user specifies the switch.
Demo of the latter:
>>> import argparse
>>> parser = argparse.ArgumentParser(
... description='My description')
>>> parser.add_argument("-r", "--remote", action='store_true', help="help message")
_StoreTrueAction(option_strings=['-r', '--remote'], dest='remote', nargs=0, const=True, default=False, type=None, choices=None, help='help message', metavar=None)
>>> parser.print_help()
usage: [-h] [-r]
My description
optional arguments:
-h, --help show this help message and exit
-r, --remote help message
>>> opts = parser.parse_args([])
>>> opts.remote
False
>>> opts = parser.parse_args(['-r'])
>>> opts.remote
True

You are missing action.
import argparse
parser = argparse.ArgumentParser(
description='My description')
parser.add_argument(
"-r", "--remote", action="store_true", # add action
help="help message")
parser.print_help()
usage: -c [-h] [-r]
My description
optional arguments:
-h, --help show this help message and exit
-r, --remote help message

Related

How can I remove three-dots at the end of usage line in argparse

Python argparse keep putting space and three-dots ( ...) at the end of usage: line, example: usage: program.sh [-h] command [<options>...] .... Would it be possible to remove them?
Example code:
def helper():
parser = argparse.ArgumentParser(
"program.py",
)
subparsers = parser.add_subparsers(dest="command", metavar="command [<options>...]")
driver = subparsers.add_parser(
"driver", help="Example script")
driver.add_argument("--bn", type=int, default=0, help="Block number to start fetch blocks from")
return parser
Output:
$ ./program.sh --help
usage: program.sh [-h] command [<options>...] ...
Direct answer: you could write your own usage summary:
parser = argparse.ArgumentParser(
"program.py",
usage="usage: %(prog)s [-h] command [<options>...]",
)
However, there is something that does not make sense to me.
The ... is caused by using subparsers. There is usually one subparser per command, for example: 3 commands, i.e. 3 subparsers:
Usage:
myprog cmd1 [options and args specific for cmd1]
myprog cmd2 [different options and args for cmd2]
myprog cmd3 [yet another set of options and args]
And that can be hardly summarized in one line except with:
Usage: myprog command ...
unless you are not using any options and args for the commands, which means there is nothing to parse.
So, if you want to get rid of the trailing ... and still have a valid usage synopsis, you probably do not need subparsers at all.
That usage is produced by the implied nargs of the subparsers argument, 'A...'.
I get the same thing if I create a positional argument with the same nargs:
In [393]: import argparse
In [394]: p = argparse.ArgumentParser()
In [395]: p.add_argument('foo',nargs=argparse.PARSER)
Out[395]: _StoreAction(option_strings=[], dest='foo', nargs='A...', const=None, default=None, type=None, choices=None, help=None, metavar=None)
In [396]: p.print_help()
usage: ipython3 [-h] foo ...
positional arguments:
foo
optional arguments:
-h, --help show this help message and exit
Note the nargs string in Out[395]. add_subparsers creates a positional argument with 'A...' nargs value.
In [403]: p = argparse.ArgumentParser()
In [404]: p.add_subparsers(dest='foo', metavar='FOO')
Out[404]: _SubParsersAction(option_strings=[], dest='foo', nargs='A...', const=None, default=None, type=None, choices={}, help=None, metavar='FOO')
In [405]: p.print_help()
usage: ipython3 [-h] FOO ...
positional arguments:
FOO
optional arguments:
-h, --help show this help message and exit
That nargs takes all the remaining strings while requiring at least one. The add_parser lines add choices to that subparser Action.
In formatting its usage the main parser "knows nothing" about what the sub-parsers do. To it, the subparsers argument is just another positional argument with choices. The same applies when parsing. It just does the suparsers.__call__ with the remaining argv strings. That in turn passes those to the chosen parser.

python ArgParse custom action with key value options using metavar causes duplicates in -h output

I'm using a custom action for argument parser to allow key=value options for specific arguments.
test.py --arg2 input1=something input2=something_else
The custom action works fine, but when I use metavar to list all of the custom options, I get duplicates.
Here is my custom action:
class KeyValue(argparse.Action):
def __call__(self, parser, namespace,
values, option_string=None):
setattr(namespace, self.dest, dict())
for value in values:
# split it into key and value
key, value = value.split('=')
# assign into dictionary
getattr(namespace, self.dest)[key] = value
Here is the utilization:
sub_1.add_argument(
"--arg2",
action=KeyValue,
nargs="*",
default=NoArgumentProvided(),
help='arg 2 help',
metavar="key1=val1 key2=val2"
)
What the help looks like, with duplicates showing up:
usage: arg_issue.py test [-h] [--arg1]
[--arg2 [key1=val1 key2=val2 [key1=val1 key2=val2 ...]]]
test description
optional arguments:
-h, --help show this help message and exit
--arg1 arg1 help
--arg2 [key1=val1 key2=val2 [key1=val1 key2=val2 ...]]
arg 2 help
Any ideas why I'm getting duplicates? It's clearly from the custom Action, but I'm not sure why?
Full Code:
import argparse
class NoArgumentProvided(object):
pass
class KeyValue(argparse.Action):
def __call__(self, parser, namespace,
values, option_string=None):
setattr(namespace, self.dest, dict())
for value in values:
# split it into key and value
key, value = value.split('=')
# assign into dictionary
getattr(namespace, self.dest)[key] = value
def main():
parser = argparse.ArgumentParser(
description='parser',
formatter_class=argparse.RawTextHelpFormatter,
)
# top-level args.
parser.add_argument('--verbose',
help='Verbose mode',
action='store_true',
required=False,
default=NoArgumentProvided())
# Add the main sub parsers
subparsers = parser.add_subparsers(dest='action')
sub_1 = subparsers.add_parser(
'test',
help='test help',
description='test description')
sub_1.add_argument(
"--arg1",
action='store_true',
required=False,
default=NoArgumentProvided(),
help='arg1 help'
)
sub_1.add_argument(
"--arg2",
action=KeyValue,
nargs="*",
default=NoArgumentProvided(),
help='arg 2 help',
metavar="key1=val1 key2=val2"
)
subparsers.required = True
args = parser.parse_args()
if __name__=="__main__":
main()
If the metavar is a tuple of strings
p.add_argument('--foo', nargs='*', metavar=('one','two'))
the help will be
usage: ipython3 [-h] [--foo [one [two ...]]]
optional arguments:
-h, --help show this help message and exit
--foo [one [two ...]]
With nargs='*', the usage is 2 parts, [one [two ...]]. Use of a custom action class doesn't change that display.
Keep the metavar simple, and elaborate in the help as needed. description can also add details.
edit
A patch a couple of years ago has simplified the * help.
https://bugs.python.org/issue38438
argparse "usage" overly-complex with nargs="*"
With Python 3.10
In [218]: import argparse310 as argparse
In [219]: p=argparse.ArgumentParser()
In [220]: p.add_argument('--foo',nargs='*');
In [221]: p.print_help()
usage: ipython3 [-h] [--foo [FOO ...]]
optional arguments:
-h, --help show this help message and exit
--foo [FOO ...]
The tuple version is still available:
In [226]: p._actions[1].metavar=('one','two')
In [227]: p.print_help()
usage: ipython3 [-h] [--foo [one [two ...]]]
optional arguments:
-h, --help show this help message and exit
--foo [one [two ...]]

Argparse: set the name of a choice arg in the command 'help'

In argparse, a choice argument can be created by using code like this:
parser = argparse.ArgumentParser()
parser.add_argument("action", type=str,
help="The action to do. Eligible values:\ninstall, remove, version", choices=['install', 'remove', 'version'])
When parser is an instance of argparse.ArgumentParser()
However, when displaying the help, instead of the arg being specified as its name, it is specified as {install,remove,version}, the whole output being
positional arguments:
{install,remove,version}
The action to do. Eligible values: install, remove,
version
optional arguments:
-h, --help show this help message and exit
How can i get his to display the name of the arg, so the output is more like
positional arguments:
action The action to do. Eligible values: install, remove,
version
optional arguments:
-h, --help show this help message and exit
The metavar parameter to add_argument is what you're looking for:
parser = argparse.ArgumentParser()
parser.add_argument(
"action",
type=str,
help="The action to do. Eligible values:\ninstall, remove, version",
choices=['install', 'remove', 'version'],
metavar="action",
)
Calling parser.print_help() yields:
usage: [-h] action
positional arguments:
action The action to do. Eligible values: install, remove, version
optional arguments:
-h, --help show this help message and exit
You can specify metavar
When ArgumentParser generates help messages, it needs some way to refer to each expected argument. By default, [...] An alternative name can be specified with metavar:
parser = argparse.ArgumentParser()
parser.add_argument("action", type=str, metavar='action',
help="The action to do. Eligible values:\ninstall, remove, version", choices=['install', 'remove', 'version'])

Adding sub-subcommands to a command

I have a python script that takes input from CLI, parses it and runs the appropriate function:
command subcommand subsubcommand1 --arg1 <value1>
When user doesn't provide subsubcommand I want the script to return an error, saying that at least one of the supported subcommand must be provided, instead it returns:
Namespace object has no attribute func
This is the code:
parser = argparse.ArgumentParser(description='The highlevel command')
parser.add_argument("-v", help="some help text", action='store_true', default=False)
subparsers = parser.add_subparsers(dest='action')
subparsers.required = True
subcommand_parser = subparsers.add_parser('subcommand', help='some help text for the subcommand')
subsub_subparser = subcommand_parser.add_subparsers()
subsubparser1 = subsub_subparser.add_parser('subsubcommand1', help='some help text for the subsubcommand1')
subsubparser1.set_defaults(action='subsubcommand_action', func=mylib.subsub1)
subsubparser1.add_argument('--arg1', required=True, help='arg1')
subsubparser1.add_argument('--arg2', required=False, default='hello', help='arg2')
How can I fix the current error and make the new error show up?
Correcting the omissions mentioned in my comments:
import argparse
parser = argparse.ArgumentParser(description='The highlevel command')
parser.add_argument("-v", help="some help text", action='store_true', default=False)
subparsers = parser.add_subparsers(dest='action')
subparsers.required = True
subcommand_parser = subparsers.add_parser('subcommand', help='some help text for the subcommand')
subsub_subparser = subcommand_parser.add_subparsers(dest='subaction') # EDIT
subsub_subparser.required = True # EDIT
subsubparser1 = subsub_subparser.add_parser('subsubcommand1', help='some help text for the subsubcommand1')
subsubparser1.set_defaults(func="mylib.subsub1") # EDIT
subsubparser1.add_argument('--arg1', required=True, help='arg1')
subsubparser1.add_argument('--arg2', required=False, default='hello', help='arg2')
subsubparser2 = subsub_subparser.add_parser('subsubcommand2') # EDIT
args = parser.parse_args()
print(args) # EDIT
if hasattr(args, 'func'):
print('func: ',args.func)
else:
print('func not defined')
sample runs:
2020:~/mypy$ python3 stack56435945.py
usage: stack56435945.py [-h] [-v] {subcommand} ...
stack56435945.py: error: the following arguments are required: action
2021:~/mypy$ python3 stack56435945.py subcommand
usage: stack56435945.py subcommand [-h] {subsubcommand1,subsubcommand2} ...
stack56435945.py subcommand: error: the following arguments are required: subaction
2021:~/mypy$ python3 stack56435945.py subcommand subsubcommand1 --arg1 foobar
Namespace(action='subcommand', arg1='foobar', arg2='hello', func='mylib.subsub1', subaction='subsubcommand1', v=False)
func: mylib.subsub1
2022:~/mypy$ python3 stack56435945.py subcommand subsubcommand2
Namespace(action='subcommand', subaction='subsubcommand2', v=False)
func not defined

Remove long sub-command help output?

I'm using argparse to produce my CLI and using the add_subparsers function. This works exception the --help output is really ugly. It lists all the commands in the overall syntax. For example:
usage: redid [-h] [--config CONFIG] [--verbose] [--show-curl] [--save [SAVE]]
{setup,show-config,check-auth,version,get-profile,put-profile,delete-profile,get-profile-signature,list-profiles,list-resources,ls-resources,get-resource-record,delete-resource,get-resource,upload-resource,get-resource-url}
...
I'd much prefer to have a more traditional and clean output similar to:
usage: redid [OPTIONS...] Command ...
How can I do this?
Try adding the metavar argument to your subparser definition and give it no value:
parser.add_subparsers(title="the_commands", metavar="")
From the documentation:
Description of parameters:
....
metavar - string presenting available sub-commands in help; by default it is None and presents sub-commands in form {cmd1, cmd2, ..}
Here's an example, I'm not sure how you've set up your sub-parsers but:
parser = argparse.ArgumentParser(prog='PROG')
parser.add_argument('--foo', action='store_true', help='foo help')
subparsers = parser.add_subparsers(help="sub-command help", metavar="sub command")
parser_a = subparsers.add_parser('a', help='a help')
parser_a.add_argument('bar', type=int, help='bar help')
parser_b = subparsers.add_parser('b', help='b help')
parser_b.add_argument('--baz', choices='XYZ', help='baz help')
>>> parser.print_help()
usage: PROG [-h] [--foo] sub command ...
positional arguments:
sub command sub-command help
a a help
b b help
optional arguments:
-h, --help show this help message and exit
--foo foo help

Categories