Permit argparse global flags after subcommand

前端 未结 3 2341
情话喂你
情话喂你 2021-02-19 18:20

I am using argparse to build a command with subcommands:

mycommand [GLOBAL FLAGS] subcommand [FLAGS]

I would like the global flags to work whether they are befor

相关标签:
3条回答
  • 2021-02-19 18:32

    This is a perfect use case for parents argparse feature:

    Sometimes, several parsers share a common set of arguments. Rather than repeating the definitions of these arguments, a single parser with all the shared arguments and passed to parents= argument to ArgumentParser can be used.

    Define a base parent ArgumentParser, add arguments that will be shared across subparsers. Then, add subparsers and set your base parser as a parent by providing parents keyword argument:

    parser = argparse.ArgumentParser()
    subparsers = parser.add_subparsers(dest='subparser_name')
    
    base_subparser = argparse.ArgumentParser(add_help=False)
    # define common shared arguments
    base_subparser.add_argument('--disable')
    
    sp = subparsers.add_parser('compile', parents=[base_subparser])
    # define custom arguments
    sp = subparsers.add_parser('launch', parents=[base_subparser])
    # define custom arguments
    

    Note that add_help=False here helps to avoid problems with conflicting help argument.

    Also see: Python argparse - Add argument to multiple subparsers.

    0 讨论(0)
  • 2021-02-19 18:43

    I see two issues in your example:

    1) The use of '--disable' in both the parser and subparsers. Nested ArgumentParser deals with that overlapping dest.

    2) The repeated set of arguments in the subparsers. parents is certainly one way to simplify that. But you could easily write your own code:

    parser = argparse.ArgumentParser()
    subparsers = parser.add_subparsers(dest='subparser_name')
    
    parser.add_argument('--disable', dest='main_disable')    # This flag...
    
    for name in ['compile', 'launch']:
        sp = subparsers.add_parser(name)
        sp.add_argument('zones', nargs='*')
        sp.add_argument('--disable', dest=name+'_disable')        # Is repeated...
    
    0 讨论(0)
  • 2021-02-19 18:59

    You are asking for argparse solution, but as you also call for a Pythonic solution, I will deliberately propose an alternative using package docopt:

    get it:

    $ pip install docopt
    

    Write your code into mycommand file:

    """
    Usage:
        mycommand compile [--disable] <zone>...
        mycommand launch  [--disable] <zone>...
    
    Arguments:
        <zone>  zone name
    
    Options:
      -h --help
      --disable  disable
    
    """
    from docopt import docopt
    
    if __name__ == "__main__":
        args = docopt(__doc__)
        print args
    

    Then call it from command line:

    basic help (no arguments):

    $ python mycommand
    Usage:
        mycommand compile [--disable] <zone>...
        mycommand launch  [--disable] <zone>...
    

    Asking for a help:

    $ python mycommand -h
    Usage:
        mycommand compile [--disable] <zone>...
        mycommand launch  [--disable] <zone>...
    
    Arguments:
        <zone>  zone name
    
    Options:
      -h --help
      --disable  disable
    

    Using compile subcommand:

    $ python mycommand compile zoneAlfa zoneBeta
    {'--disable': False,
     '<zone>': ['zoneAlfa', 'zoneBeta'],
     'compile': True,
     'launch': False}
    

    add flag --disable:

    $ python mycommand compile --disable zoneAlfa zoneBeta
    {'--disable': True,
     '<zone>': ['zoneAlfa', 'zoneBeta'],
     'compile': True,
     'launch': False}
    

    launch subcommand works too:

    $ python mycommand launch --disable zoneAlfa zoneBeta
    {'--disable': True,
     '<zone>': ['zoneAlfa', 'zoneBeta'],
     'compile': False,
     'launch': True}
    

    My usage of argument parsers started with argparse too, and I hated the complexity of code, which was needed for doing something.

    Later I turned into plac, which is very efficient way for turning a python function into very usable command for console.

    Still, I was limited by set of rules to follow and understand, which were not very clear to me in many cases. With docopt I appreciate, that I can write the docstring first, following standard rules of POSIX, and then use it in my code. If you would care about validation of arguments, I will direct you to samples of this great package.

    0 讨论(0)
提交回复
热议问题