Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Python argparse optional sub-arguments

I'd like to have an argument to my program that has some required parameters along with some optional parameters. Something like this:

[--print text [color [size]]

so you could pass it any of these:

mycommand --print hello
mycommand --print hello blue
mycommand --print hello red 12

There could be multiple of these so it has to be a single add_argument. For example:

[--print text [color]] [--output filename [overwrite]]

I can achieve arguments that are close to what I want:

>>> parser = argparse.ArgumentParser()
>>> act = parser.add_argument('--foo', nargs=3, metavar=('x','y','z'))
>>> act = parser.add_argument('--bar', nargs='?')
>>> act = parser.add_argument('--baz', nargs='*')
>>> parser.print_help()
usage: [-h] [--foo x y z] [--bar [BAR]] [--baz [BAZ [BAZ ...]]]

optional arguments:
  -h, --help            show this help message and exit
  --foo x y z
  --bar [BAR]
  --baz [BAZ [BAZ ...]]

but not quite. Is there any way to do this with argparse? I know I could make them all nargs="*" but then --help would not list the names of the optional arguments. If I pass nargs="*" and a tuple for metavar, argparse throws an exception.

like image 699
jterrace Avatar asked Mar 10 '11 09:03

jterrace


People also ask

How do you add an optional argument in Argparse?

To add an optional argument, simply omit the required parameter in add_argument() . args = parser. parse_args()if args.

How do you make an argument compulsory in Python?

required is a parameter of the ArugmentParser object's function add_argument() . By default, the arguments of type -f or --foo are optional and can be omitted. If a user is required to make an argument, they can set the keyword argument required to True .

What is Store_true in Python?

The store_true option automatically creates a default value of False. Likewise, store_false will default to True when the command-line argument is not present. The source for this behavior is succinct and clear: http://hg.python.org/cpython/file/2.7/Lib/argparse.py#l861.

What is Metavar?

Metavar: It provides a different name for optional argument in help messages.


3 Answers

that will work for single arg:

parser.add_argument('--write_google', nargs='?', const='Yes',
                    choices=['force', 'Yes'],
                    help="write local changes to google")
like image 193
Lareb Kinsky Avatar answered Oct 20 '22 01:10

Lareb Kinsky


How about

def printText(args):
  print args

parser = argparse.ArgumentParser()
subparser = parser.add_subparsers()
printer = subparser.add_parser('print')
printer.add_argument('text')
printer.add_argument('color', nargs='?')
printer.add_argument('size', type=int, nargs='?')
printer.set_defaults(func=printText)

cmd = parser.parse_args()
cmd.func(cmd)

Then you get something like this:

$ ./test.py -h
usage: test.py [-h] {print} ...

positional arguments:
  {print}

$ ./test.py print -h
usage: test.py print [-h] text [color] [size]

positional arguments:
  text
  color
  size

$ ./test.py print text
Namespace(color=None, func=<function printText at 0x2a96150b90>, size=None, text='text')

$ ./test.py print text red
Namespace(color='red', func=<function printText at 0x2a96150b90>, size=None, text='text')

$ ./test.py print text red 12
Namespace(color='red', func=<function printText at 0x2a96150b90>, size=12, text='text')
like image 25
CNeo Avatar answered Oct 20 '22 02:10

CNeo


Reading the source code (start in take_action), I believe what you want is impossible. All argument parsing and passing to actions is done based on nargs, and nargs is either a number, OPTIONAL ("?"), ZERO_OR_MORE ("*"), ONE_OR_MORE ("+"), PARSER, or REMAINDER. This must be determined before the Action object (which handles the input) even sees what it's getting, so it can't dynamically figure out nargs.

I think you'll need to live with a workaround. I would maybe have --foo-x x, --foo-y y, and --foo-z z, and perhaps also --foo x y z.

like image 7
Devin Jeanpierre Avatar answered Oct 20 '22 01:10

Devin Jeanpierre