Search code examples
pythoncommand-line-argumentsargparse

How to parse multiple nested sub-commands using python argparse?


I am implementing a command line program which has interface like this:

cmd [GLOBAL_OPTIONS] {command [COMMAND_OPTS]} [{command [COMMAND_OPTS]} ...]

I have gone through the argparse documentation. I can implement GLOBAL_OPTIONS as optional argument using add_argument in argparse. And the {command [COMMAND_OPTS]} using Sub-commands.

From the documentation it seems I can have only one sub-command. But as you can see I have to implement one or more sub-commands. What is the best way to parse such command line arguments useing argparse?


Solution

  • @mgilson has a nice answer to this question. But problem with splitting sys.argv myself is that I lose the nice help message Argparse generates for the user. So I ended up doing this:

    import argparse
    
    ## This function takes the 'extra' attribute from global namespace and
    ## re-parses it to create separate namespaces for all other chained commands.
    def parse_extra (parser, namespace):
      namespaces = []
      extra = namespace.extra
      while extra:
        n = parser.parse_args(extra)
        extra = n.extra
        namespaces.append(n)
    
      return namespaces
    
    argparser=argparse.ArgumentParser()
    subparsers = argparser.add_subparsers(help='sub-command help', dest='subparser_name')
    
    parser_a = subparsers.add_parser('command_a', help = "command_a help")
    ## Setup options for parser_a
    
    ## Add nargs="*" for zero or more other commands
    argparser.add_argument('extra', nargs = "*", help = 'Other commands')
    
    ## Do similar stuff for other sub-parsers
    

    Now after first parse all chained commands are stored in extra. I reparse it while it is not empty to get all the chained commands and create separate namespaces for them. And i get nicer usage string that argparse generates.