3

So I'm trying to come up with a strategy using the argparse library.

Here's how I want to interface with my program:

$ program list [<number>] 
$ program check <name>
$ program watch <name> [<quality>]

Right now I have an argument parser like the following:

parser = argparse.ArgumentParser()
group = parser.add_mutually_exclusive_group()
group.add_argument('list')
group.add_argument('check')
group.add_argument('watch')

But how can I add an optional argument, say an integer, to an existing argument?

Meaning the user could invoke the list command in the following ways:

program list 

Where the list action would be called with a default value, or:

program list 10

Where the list action would be called with an argument of 10.

I saw the subcommands option in the documentation, but I ran into the problem where I would have a sub parser for list arguments, but then I would have to add a flag, such as -n and then provide the number. Perhaps this is a better way of doing it? But I like the idea of just providing the number if you want to, or omitting it if you don't.

Is what I'm trying to achieve good practice? Is it possible with argparse?

Spencer Wood
  • 610
  • 6
  • 15

1 Answers1

2

This sample set me off in the wrong direction. I've sketched a subparser implementation at the end that I think does the trick.

parser = argparse.ArgumentParser()
group = parser.add_mutually_exclusive_group()
group.add_argument('list')
group.add_argument('check')
group.add_argument('watch')

It expects 3 strings, and will assign them to the 3 attributes.

However, you cannot put 3 'positional` arguments in a mutually exclusive group. One optional positional (yes the terminology is confusing) can be in such a group, but the rest must 'optionals' (flagged).


Going back to your initial list. Are these different patterns that you'd like to accept

program list [integer]
program check name
program watch name [quality]

where 'list','check','watch' are literal strings, while 'integer','name','quality' are variable names.

If that is the case, then subparsers is probably the best choice. nargs='?' can be used to make positional arguments 'optional'.

parser = argparse.ArgumentParser()
sp = parser.add_subparsers(dest='cmd')  # put the command string in `cmd` attribute
p1 = sp.add_parser('list')
p2 = sp.add_parser('check')
p3 = sp.add_parser('watch')
p1.add_argument('value',dtype=int, nargs='?') # ok with 0 or 1 values
p2.add_argument('name')
p3.add_argument('name')
p3.add_argument('quality',nargs='?')

value and quality will get the default value if none is explicitly given. The default default is None. But you can define a meaningful alternative, e.g. default=0 for the integer value.


You could also define a parser like:

parser = ...
parser.add_argument('command', choices=['list','check','watch'])
parser.add_argument('rest', nargs='*')

This will expect one of the 3 'command' strings, and put anything else in the 'rest' attribute (as a list of strings). You could then interpret those strings however you want.

kindall
  • 178,883
  • 35
  • 278
  • 309
hpaulj
  • 221,503
  • 14
  • 230
  • 353