Python dependencies between groups using argparse

前端 未结 3 1685
轻奢々
轻奢々 2021-02-09 13:13

I started to learn Python, and now I\'m learning the great benefits of argparse. Using argparse, I have created two groups of arguments: group_li

相关标签:
3条回答
  • 2021-02-09 13:47

    Use Docopt! You shouldn't have to write a usage doc and then spend hours trying to figure out how to get argparse to create it for you. If you know POSIX you know how to interpret a usage doc because it is a standard. Docopt know how to interpret usage docs that same as you do. We don't need an abstraction layer.

    I think the OP has failed to describe their own intentions based on what I read in their help text. I'm going to try and speculate what they are trying to do.


    test.py

    """
    usage: test.py [-h | --version]
           test.py [-v] (-m | -p)
           test.py [-v] --list (modules | ports)
           test.py [-v] (-M <module_name> | -P <fc_port_name> | -I <iSCSI_port_name>)
    
    this is the description
    
    optional arguments:
      -h, --help                show this help message and exit
      -v, --verbose             verbose
      -m                        list only modules (same as --list modules)
      -p                        list only ports   (same as --list ports)
      --list                    list only module or ports
      -M module_name            simulate module down
      -P fc_port_name           simulate FC port down
      -I iSCSI_port_name        simulate iSCSI port down
    
    This is the epilog
    """
    
    from pprint import pprint
    from docopt import docopt
    
    def cli():
        arguments = docopt(__doc__, version='Super Tool 0.2')
        pprint(arguments)
    
    if __name__ == '__main__':
        cli()
    

    While it would be possible to communicate all of the usage in a single line with complex nested conditionals, this is more legible. This is why docopt makes so much sense. For a CLI program you want to make sure you communicate to the user clearly. Why learn some obscure module syntax in the hope that you can convince it to create the communication to the user for you? Take the time to look at other POSIX tools with option rules similar to your needs and copy-pasta.

    0 讨论(0)
  • 2021-02-09 13:48

    You can use a common mutually-exclusive-group as "root" of the two subgroups:

    import argparse
    parser = argparse.ArgumentParser(
            description='this is the description',
            epilog="This is the epilog",
            argument_default=argparse.SUPPRESS  
            )
    
    parser.add_argument('-v', '--verbose', help='verbose', action='store_true', default=False)
    
    root_group = parser.add_mutually_exclusive_group()
    
    group_list = root_group.add_mutually_exclusive_group()
    group_list.add_argument('-m', help='list only modules', action='store_const', dest='list', const='modules', default='all')
    group_list.add_argument('-p', help='list only ports', action='store_const', dest='list', const='ports', default='all')
    group_list.add_argument('--list', help='list only module or ports', choices=['modules','ports'], metavar='<modules/ports>', default='all')
    
    group_simulate = root_group.add_mutually_exclusive_group()
    group_simulate.add_argument('-M', help='simulate module down', nargs=1, metavar='module_name', dest='simulate')
    group_simulate.add_argument('-P', help='simulate FC port down', nargs=1, metavar='fc_port_name', dest='simulate')
    group_simulate.add_argument('-I', help='simulate iSCSI port down', nargs=1, metavar='iSCSI_port_name', dest='simulate')
    group_simulate.add_argument('--simulate', help='simulate module or port down', nargs=1, dest='simulate')
    
    args = parser.parse_args()
    
    print args
    

    Result:

    $ python test.py -m -P asfafs
    usage: test.py [-h] [-v] [[-m | -p | --list <modules/ports>]
                    [-M module_name | -P fc_port_name | -I iSCSI_port_name | --simulate SIMULATE]
    test.py: error: argument -P: not allowed with argument -m 
    
    $ python test.py -m -p
    usage: test.py [-h] [-v] [[-m | -p | --list <modules/ports>]
                    [-M module_name | -P fc_port_name | -I iSCSI_port_name | --simulate SIMULATE]
    test.py: error: argument -p: not allowed with argument -m
    
    0 讨论(0)
  • 2021-02-09 13:57

    A simpler version of this parser is

    parser=argparse.ArgumentParser(description="this is the description",
                    epilog='this is the epilog')
    parser.add_argument('-v', '--vebose', action='count')
    g1=parser.add_mutually_exclusive_group()
    g1.add_argument('--list', help='list module or ports (default=%(default)s)', choices=['modules','ports','all'], default='all')
    g1.add_argument('--simulate', '-M','-P','-C', help='simulate [module down/ FS port down/ iSCSI port down]', dest='simulate', metavar='module/port')
    

    With a help that looks like:

    usage: stack14660876.py [-h] [-v]
                            [--list {modules,ports,all} | --simulate module/port]
    
    this is the description
    
    optional arguments:
      -h, --help            show this help message and exit
      -v, --vebose
      --list {modules,ports,all}
                            list module or ports (default=all)
      --simulate module/port, -M module/port, -P module/port, -C module/port
                            simulate [module down/ FS port down/ iSCSI port down]
    
    this is the epilog
    

    Beside verbose (here I substituted a count) the OP sets to attributes, list and simulate. list has a default value of all, and can be set to modules or ports. -m and -p are just short cuts, and don't really add to the definition. Shortcuts can be handy when defining lots of options, especially if the options can be used together (e.g. -vpm). But here only one option is allowed (besides -v).

    simulate takes an unconstrained string. The M/P/C options are just documentation conveniences, and don't constrain the values or add meaning.

    This is a nice exercise in pushing the boundaries of argparse (or any other parser), but I think it is too complicated to be useful. Despite all the groupings it comes down to allowing only one option.

    ==========================

    Comments about docopt and POSIX argument handling prompted me to look at C argument libraries. getopt is the old standard. Python has a functional equivalent, https://docs.python.org/2/library/getopt.html

    The other parser in the GNU library is argp.

    http://www.gnu.org/software/libc/manual/html_node/Argp.html

    I haven't seen, yet, a clear description of what it adds to the getopt syntax. But the following paragraph is interesting.

    Argp also provides the ability to merge several independently defined option parsers into one, mediating conflicts between them and making the result appear seamless. A library can export an argp option parser that user programs might employ in conjunction with their own option parsers, resulting in less work for the user programs. Some programs may use only argument parsers exported by libraries, thereby achieving consistent and efficient option-parsing for abstractions implemented by the libraries.

    It sounds a bit like the argparse subparser mechanism. That is, there's some sort of meta-parser that can delegate the action to one (or more) subparsers. But in argparse subparsers have to be explicitly named by the user.

    A possible extension is to have the meta-parser look at the context. For example in the OP case, if it sees any of [--list, -p, -m] use the list subparser, if any of the simulate arguments, use the simulate subparser. That might give some more powerful grouping tools. And it might be possible to implement that sort of thing with the stock argparse. You can create and run several different parsers on the same sys.argv.

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