Argparse Positional arguments inside an Optional argument - python

I want the following structure with argparse :
usage: ms1.py [-h] [-c] [-u] [password] [DB_name]
optional arguments:
-h, --help show this help message and exit
-c, --c show available items
-u password DB_name, Update database
I wish to place -h -c -u all as optional.
but if -u was declared by user - password & DB_name must be positional to it.
What is the right code ? (I'm so confused by the documentation, many thanks)

Something like this should work for you:
from argparse import ArgumentParser
p = ArgumentParser()
p.add_argument('--c', required=False)
p.add_argument('--u', required=False)
p.add_argument('password')
p.add_argument('db_name')
args = p.parse_args()
print(args)
You don't need to have -h. argparse does it for you at no extra cost.
c and u are optional and password, db_name are required and the position matter.

import argparse
parser = argparse.ArgumentParser()
parser.add_argument('-c', action='store_true', help='show available items')
parser.add_argument('-u', nargs=2, metavar=('password', 'DB_name'), help='Update database')
args = parser.parse_args()
print(args)
samples:
1442:~/mypy$ python3 stack62967549.py -h
usage: stack62967549.py [-h] [-c] [-u password DB_name]
optional arguments:
-h, --help show this help message and exit
-c show available items
-u password DB_name Update database
1442:~/mypy$ python3 stack62967549.py -c
Namespace(c=True, u=None)
1443:~/mypy$ python3 stack62967549.py -u foobar FOO
Namespace(c=False, u=['foobar', 'FOO'])

Related

Put subparser command at the beginning of arguments

I wanted to create an interface with argparse for my script with subcommands; so, if my script is script.py, I want to call it like python script.py command --foo bar, where command is one of the N possible custom commands.
The problem is, I already tried looking for a solution here on StackOverflow, but it seems like everything I tried is useless.
What I have currently is this:
parser = argparse.ArgumentParser()
parser.add_argument("-x", required=True)
parser.add_argument("-y", required=True)
parser.add_argument("-f", "--files", nargs="+", required=True)
# subparsers for commands
subparsers = parser.add_subparsers(title="Commands", dest="command")
subparsers.required = True
summary_parser = subparsers.add_parser("summary", help="help of summary command")
If I try to run it with:
args = parser.parse_args("-x 1 -y 2 -f a/path another/path".split())
I got this error, as it should be: script.py: error: the following arguments are required: command.
If, however, I run this command:
args = parser.parse_args("summary -x 1 -y 2 -f a/path another/path".split())
I got this error, that I shouldn't have: script.py: error: the following arguments are required: -x, -y, -f/--files.
If I put the command at the end, changing also the order of arguments because of -f, it works.
args = parser.parse_args("-x 1 -f a/path another/path -y 2 summary".split())
If I add the parents keyword in subparser, so substitute the summary_parser line with summary_parser = subparsers.add_parser("summary", help=HELP_CMD_SUMMARY, parents=[parser], add_help=False), then I got:
script.py summary: error: the following arguments are required: command when summary is in front of every other argument;
script.py summary: error: the following arguments are required: -x, -y, -f/--files, command when summary is at the end of the args.
My question is, how I have to setup the parsers to have the behaviour script.py <command> <args>? Every command shares the same args, because they are needed to create certain objects, but at the same time every command can needs other arguments too.
Creating another parser helped me getting what I wanted.
The root parser should add all the optional arguments - and also have add_help=False, to avoid an help message conflict -, then another parser - parser2, with a lot of fantasy - will be created.
The second parser will have subparsers, and they all needs to specify as parents the root parser.
parser = argparse.ArgumentParser(add_help=False)
parser.add_argument() ...
parser2 = argparse.ArgumentParser()
subparsers = parser2.add_subparsers(title="Commands", dest="command")
subparsers.required = True
summary_parser = subparsers.add_parser("summary", parents=[parser])
summary_parser.add_argument("v", "--verbose", action="store_true")
# parse args
args = parser2.parse_args()
Now the output will be this:
usage: script.py [-h] {summary} ...
optional arguments:
-h, --help show this help message and exit
Commands:
{summary}
summary For each report print its summary, then exit
usage: script.py summary [-h] -x X -y Y -f FILES [FILES ...] [-v]
optional arguments:
-h, --help show this help message and exit
-x X
-y Y
-f FILES [FILES ...], --files FILES [FILES ...]
-v, --verbose

Extra Characters in python Argparse?

I have an argparse function:
def argParse():
# Possible Arguments
parser = argparse.ArgumentParser()
parser.add_argument("-s", "--search", dest="s", help="Seach parameters")
parser.add_argument("-d", "--directory", dest="d", help="Choose Directory to save data")
args = parser.parse_args()
params = str(args.s)
directory = str(args.d)
if params == "None":
print("Search parameters are null, exiting program.")
parser.print_help()
exit(1)
if directory == "None":
print("directory is null, storing to current working directory.")
But, it is adding a extra capitol S and D to the useage output? Any idea on how to clean this up?
usage: scan.py [-h] [-s S] [-d D]
optional arguments:
-h, --help show this help message and exit
-s S, --search S Seach parameters
-d D, --directory D Choose Directory to save data
I think you're looking for the metavar kwarg.
try this:
import argparse
def parse_args():
parser = argparse.ArgumentParser()
parser.add_argument('-s', '--search', dest='s', metavar='\b', help='Search parameters')
args = parser.parse_args()
parse_args()
\b is the backspace character, so it also removes the space between the two,
as opposed to using "" which I beleive would still leave a space after the initial variable name.
output of python argparse_test.py -h:
usage: argparse_test.py [-h] [-s]
optional arguments:
-h, --help show this help message and exit
-s, --search Search parameters

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

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

argparse - Reproduce behavior of "--help" argument with a specific argument

I have some trouble while working with the argparse module for Python v2.7.
Basically, what I have is a script that works with 5 mandatory arguments :
url
method
login
password
output
An example of the syntax would look like this :
script.py -w/--url [URL] -m/--method [METHOD] -l/--login [LOGIN] -p/--password [PASSWORD] -o/--output [OUTPUT]
What I'd like to do is this :
add an optional argument -t/--test
its behavior would be that, based on the url used with the -w/--url argument, it would bypass completely the -m/--method, -l/--login and -p/--password arguments but, for it to work, I need to tell argparse to stop processing arguments if -t/--test is provided (but only with -w/--url).
Is this behavior even possible? I tried to play with argparse sub-commands but it seems to be (at least to my small knowledge) a bit overkill.
NB: Here is my original code :
# Description : parses script arguments
# Argument(s) : all
# Return value : all arguments values
def testArgs():
parser = argparse.ArgumentParser(description='Foo', formatter_class=argparse.RawDescriptionHelpFormatter)
parser.add_argument('-w','--url', help='URL', required=True)
parser.add_argument('-t','--test', help='Test command', action='store_true')
parser.add_argument('-m','--method', help='METHOD', required=True)
parser.add_argument('-u','--login_name', help='LOGIN', required=True)
parser.add_argument('-p','--login_password', help='PASSWORD', required=True)
parser.add_argument('-o','--output_format', help='OUTPUT', required=True, choices=['json', 'yaml', 'python'], default='json')
args = parser.parse_args()
return args
EDIT : After a lot of testing, I have managed the following :
def testArgs():
parser = argparse.ArgumentParser(description='DESCRIPTION')
subparsers = parser.add_subparsers()
p_list = subparsers.add_parser('test', help='List all available methods')
p_list.add_argument('-w', help='URL', required=True)
p_list.add_argument('-t', help='Test', action='store_true', required=True)
p_cmd = subparsers.add_parser('cmd', help='Executes command')
p_cmd.add_argument('-w', help='URL', required=True)
p_cmd.add_argument('-m', help='Method', required=True)
p_cmd.add_argument('-l', help='Login', required=True)
p_cmd.add_argument('-p', help='Password', required=True)
p_cmd.add_argument('-o', help='Output', required=True)
args = parser.parse_args()
return args
Which exhibits the following behavior :
$ python testArgparse.py -h
usage: testArgeparse.py [-h] {test,cmd} ...
DESCRIPTION
positional arguments:
{test,cmd}
test Lists all available methods
cmd Executes command
optional arguments:
-h, --help show this help message and exit
But to access help on the others arguments, I need to do the following :
$ python testArgparse.py test -h
usage: testArgparse.py test [-h] -w W -t
optional arguments:
-h, --help show this help message and exit
-w W URL
-t Test
$ python testArgparse.py cmd -h
usage: testArgparse.py cmd [-h] -w W -m M -l L -p P -o O
optional arguments:
-h, --help show this help message and exit
-w W URL
-m M Method
-l L Login
-p P Password
-o O Output
I'd like to be able to, at least, display help about all arguments without having to use --help for both test and cmd arguments.
Ideally, what I'd like is this behavior :
$ python testArgparse.py [-w URL -t] | [-w URL -m METHOD -u LOGIN -p PASS -o OUTPUT]
required=True with store_true does not make sense. The default is False, but if it is required the returned value will always be True.
Since only -w is required unconditionally, I would drop the required parameter on everything else. Then test for the required values after parse_args. I can still issue an argparse error with usage at that time. In other words, do my own testing rather than try something fancy in argparse.
def testArgs():
usage = '[-w URL -t] | [-w URL -m METHOD -u LOGIN -p PASS -o OUTPUT]'
parser = argparse.ArgumentParser(description='DESCRIPTION',usage=usage)
parser.add_argument('-w', help='URL', required=True)
parser.add_argument('-t', help='Test', action='store_true')
parser.add_argument('-m', help='Method')
parser.add_argument('-l', help='Login')
parser.add_argument('-p', help='Password')
parser.add_argument('-o', help='Output')
args = parser.parse_args()
# sample test, streamline and refine to suit your needs
# this assumes the default for all these args is None
if not args.t: # '-t' in in argv
if any([args.m is None, args.l is None, args.p is None, args.o is None]):
parser.error('m,l,p,o are all required')
return args
1216:~/mypy$ python2.7 stack21070971.py
usage: [-w URL -t] | [-w URL -m METHOD -u LOGIN -p PASS -o OUTPUT]
stack21070971.py: error: argument -w is required
1213:~/mypy$ python2.7 stack21070971.py -w url
usage: [-w URL -t] | [-w URL -m METHOD -u LOGIN -p PASS -o OUTPUT]
stack21070971.py: error: m,l,p,o are all required
1213:~/mypy$ python2.7 stack21070971.py -w url -t
Namespace(l=None, m=None, o=None, p=None, t=True, w='url')
1214:~/mypy$ python2.7 stack21070971.py -w url -m mmm
usage: [-w URL -t] | [-w URL -m METHOD -u LOGIN -p PASS -o OUTPUT]
stack21070971.py: error: m,l,p,o are all required
...
1215:~/mypy$ python2.7 stack21070971.py -w url -m mmm -l uuu -p ppp -o ooo
Namespace(l='uuu', m='mmm', o='ooo', p='ppp', t=False, w='url')

Using mutually exclusive between groups

I'm trying to have a mutually exclusive group between different groups:
I have the arguments -a,-b,-c, and I want to have a conflict with -a and -b together, or -a and -c together. The help should show something like [-a | ([-b] [-c])].
The following code does not seem to do have mutually exclusive options:
import argparse
parser = argparse.ArgumentParser(description='My desc')
main_group = parser.add_mutually_exclusive_group()
mysub_group = main_group.add_argument_group()
main_group.add_argument("-a", dest='a', action='store_true', default=False, help='a help')
mysub_group.add_argument("-b", dest='b', action='store_true',default=False,help='b help')
mysub_group.add_argument("-c", dest='c', action='store_true',default=False,help='c help')
parser.parse_args()
Parsing different combinations - all pass:
> python myscript.py -h
usage: myscript.py [-h] [-a] [-b] [-c]
My desc
optional arguments:
-h, --help show this help message and exit
-a a help
> python myscript.py -a -c
> python myscript.py -a -b
> python myscript.py -b -c
I tried changing the mysub_group to be add_mutually_exclusive_group turns everything into mutually exclusive:
> python myscript.py -h
usage: myscript.py [-h] [-a | -b | -c]
My desc
optional arguments:
-h, --help show this help message and exit
-a a help
-b b help
-c c help
How can I add arguments for [-a | ([-b] [-c])]?
So, this has been asked a number of times. The simple answer is "with argparse, you can't". However, that's the simple answer. You could make use of subparsers, so:
import argparse
parser = argparse.ArgumentParser(description='My desc')
sub_parsers = parser.add_subparsers()
parser_a = sub_parsers.add_parser("a", help='a help')
parser_b = sub_parsers.add_parser("b", help='b help')
parser_b.add_argument("-c", dest='c', action='store_true',default=False,help='c help')
parser.parse_args()
You then get:
$ python parser -h
usage: parser [-h] {a,b} ...
My desc
positional arguments:
{a,b}
a a help
b b help
optional arguments:
-h, --help show this help message and exit
and:
$ python parser b -h
usage: parser b [-h] [-c]
optional arguments:
-h, --help show this help message and exit
-c c help
If you would prefer your arguments as stated in the question, have a look at docopt, it looks lovely, and should do exactly what you want.
argument_groups don't affect the parsing. They just contribute to the help formatting. So defining a group within an mutually_exclusive_group does not help with this problem.
There is a proposed patch, http://bugs.python.org/issue10984, 'argparse add_mutually_exclusive_group should accept existing arguments to register conflicts', that would allow you to define two mutually_exclusive_groups, one [-a | -b] and the other [-a | -c]. Creating a 2nd group that includes an argument (-a) that is already defined is the trivial part of this patch. Producing a meaningful usage line is more difficult, and required a rewrite of several HelpFormatter methods.
import argparse
parser = argparse.ArgumentParser(description='My desc')
group1 = parser.add_mutually_exclusive_group()
action_a = group1.add_argument("-a", dest='a', action='store_true', default=False, help='a help')
group1.add_argument("-b", dest='b', action='store_true',default=False,help='b help')
group2 = parser.add_mutually_exclusive_group()
group2.add_argument("-c", dest='c', action='store_true',default=False,help='c help')
group2._group_actions.append(action_a) # THE KLUDGE
print parser.format_usage()
# usage: stack16769409.py [-h] [-a | -b] [-c]
args = parser.parse_args()
Usage does not show the 2 groups correctly. But it does accept -b -c, while objecting to -a -b and -a -c. But you can write a custom usage line.

Categories