19

I am adding subparsers to my parser to simulate subcommands functionality (for example code see: Simple command line application in python - parse user input?). Now I want to add a quit subparser/command that takes no arguments and has a "quit" action attached. Is it possible ? How can I go about it ?

Community
  • 1
  • 1
Mr_and_Mrs_D
  • 32,208
  • 39
  • 178
  • 361

1 Answers1

26

The documentation for subcommands gives two examples of how to identify the subparser.

https://docs.python.org/dev/library/argparse.html#sub-commands

One is to give the add_subparsers a dest:

def do_quit(args):
    # action
    quit()

parser = ArgumentParser()
subparser = parser.add_subparsers(dest='cmd')
....
subparser.add_parser('quit')
...
args = parser.parse_args()
print args.cmd   # displays 'quit'
if args.cmd == 'quit':
   do_quit(args)

the other is to use set_defaults to link the subparser with a function:

parser = ArgumentParser()
subparsers = parser.add_subparsers()
...
parser_quit = subparsers.add_parser('quit')
parser_quit.set_defaults(func=do_quit)
...
args = parser.parse_args()
args.func(args)

On further thought, here's a way, using a custom Action. It is like _HelpAction (which is used by a -h). It's called by a positional argument with nargs=0 (or '?'). Such an argument is always called, even though there are no strings to match it (or rather, 0 strings match it). This a logical, but somewhat obscure, consequence of how positionals are handled.

class QuitAction(argparse.Action):
    def __call__(self, parser, *args, **kwargs):
        parser.exit(message="QUITTING\n")

p=argparse.ArgumentParser()
sp=p.add_subparsers(dest='cmd')
p1=sp.add_parser('quit')
p1.add_argument('foo', action=QuitAction, nargs='?', help=argparse.SUPPRESS)
p.parse_args(['quit'])

producing (when run in Ipython):

QUITTING
An exception has occurred, use %tb to see the full traceback.    
SystemExit: 0
hpaulj
  • 221,503
  • 14
  • 230
  • 353
  • Thanks! If you have a look at the linked code: http://stackoverflow.com/questions/25332925/simple-command-line-application-in-python-parse-user-input/25368374#25368374 you will see that I want the parsing to take place automatically - I do not want to add logic outside the parser. No other way ? Can I add a "quit" argument to the main parser (no `-quit` that is) ? edit: [github](https://github.com/Utumno/psync/blob/master/watcher/cli.py) – Mr_and_Mrs_D Aug 31 '14 at 21:43
  • Sure - a `ArgumentParser` subclass that redefines `parse_args`. It would first call `super().parse_args` (or `parse_known_args`) and then perform the 'logic outside the parser'. Only you would know exactly what is hidden behind that method definition. – hpaulj Aug 31 '14 at 22:41
  • I added a method using an optional postional argument. – hpaulj Sep 01 '14 at 00:41
  • Thanks ! I already have a parse method - I can do that. Re: the nargs hack: how can I then prevent the `foo` argument to show up in `quit`'s help ? – Mr_and_Mrs_D Sep 01 '14 at 07:26
  • Hmm - I get `usage: sync quit [-h] dummy sync quit: error: too few arguments` in Python 2.7.8 (dummy is foo, sync is the prog) - EDIT: `nargs='?'` fixed that ! - you should edit your answer probably – Mr_and_Mrs_D Sep 01 '14 at 07:31
  • And `argparse.SUPPRESS` is the answer to my other question - feel free to add it to your answer! – Mr_and_Mrs_D Sep 01 '14 at 07:40