Argparse - How to Specify a Default Subcommand

后端 未结 2 595
误落风尘
误落风尘 2021-01-01 12:32

I am using the argparse package of Python 2.7 to write some option-parsing logic for a command-line tool. The tool should accept one of the following arguments:

\"ON

相关标签:
2条回答
  • 2021-01-01 13:13

    There are two problems with your approach.

    First you probably already noticed that newstate is not some sub_value of the sub parser and needs to be addressed at the top level of args as args.newstate. That should explain that assigning a default to newstate twice will result in the first value being overwritten. Whether you call your programm with 'ON' or 'OFF' as a parameter, each time set_state() will be called with OFF. If you just want to be able to do python cvsSecure ON and python cvsSecure OFF the following would work:

    from __future__ import print_function
    
    import sys
    import argparse
    
    def set_state(state):
        print("set_state", state)
    
    def do_on(args):
        set_state('ON')
    
    def do_off(args):
        set_state('OFF')
    
    parser = argparse.ArgumentParser()
    subparsers = parser.add_subparsers()
    
    parser_on = subparsers.add_parser('ON')
    parser_on.set_defaults(func=do_on)
    parser_on.add_argument('--fast', action='store_true')
    parser_off = subparsers.add_parser('OFF')
    parser_off.set_defaults(func=do_off)
    
    args = parser.parse_args()
    args.func(args)
    

    The second problem is that argparse does handle subparsers as single value arguments, so you have to specify one before invoking parser.parse_args(). You can automate insertion of a lacking argument by adding a extra subparser 'PRINT' and automatically inserting that using set_default_subparser added to argparse.ArgumentParser() (that code is part of the package ruamel.std.argparse

    from __future__ import print_function
    
    import sys
    import argparse
    
    def set_default_subparser(self, name, args=None):
        """default subparser selection. Call after setup, just before parse_args()
        name: is the name of the subparser to call by default
        args: if set is the argument list handed to parse_args()
    
        , tested with 2.7, 3.2, 3.3, 3.4
        it works with 2.6 assuming argparse is installed
        """
        subparser_found = False
        for arg in sys.argv[1:]:
            if arg in ['-h', '--help']:  # global help if no subparser
                break
        else:
            for x in self._subparsers._actions:
                if not isinstance(x, argparse._SubParsersAction):
                    continue
                for sp_name in x._name_parser_map.keys():
                    if sp_name in sys.argv[1:]:
                        subparser_found = True
            if not subparser_found:
                # insert default in first position, this implies no
                # global options without a sub_parsers specified
                if args is None:
                    sys.argv.insert(1, name)
                else:
                    args.insert(0, name)
    
    argparse.ArgumentParser.set_default_subparser = set_default_subparser
    
    
    def print_state(args):
        print("print_state")
    
    def set_state(state):
        print("set_state", state)
    
    def do_on(args):
        set_state('ON')
    
    def do_off(args):
        set_state('OFF')
    
    parser = argparse.ArgumentParser()
    subparsers = parser.add_subparsers()
    
    parser_print = subparsers.add_parser('PRINT', help='default action')
    parser_print.set_defaults(func=print_state)
    parser_on = subparsers.add_parser('ON')
    parser_on.set_defaults(func=do_on)
    parser_on.add_argument('--fast', action='store_true')
    parser_off = subparsers.add_parser('OFF')
    parser_off.set_defaults(func=do_off)
    
    parser.set_default_subparser('PRINT')
    
    args = parser.parse_args()
    args.func(args)
    

    You don't need to handle in args to do_on(), etc., but it comes in handy if you start specifying options to the different subparsers.

    0 讨论(0)
  • 2021-01-01 13:24

    The defaults of the top-level parser override the defaults on the sub-parsers, so setting the default value of func on the sub-parsers is ignored, but the value of newstate from the sub-parser defaults is correct.

    I don't think you want to use subcommands. Subcommands are used when the available options and positional arguments change depending on which subcommand is chosen. However, you have no other options or positional arguments.

    The following code seems to do what you require:

    import argparse
    
    def print_state():
        print "Print state"
    
    def set_state(s):
        print "Setting state to " + s
    
    parser = argparse.ArgumentParser()
    parser.add_argument('state', choices = ['ON', 'OFF'], nargs='?')
    
    args = parser.parse_args()
    
    if args.state is None:
        print_state()
    elif args.state in ('ON', 'OFF'):
        set_state(args.state)
    

    Note the optional parameters to parser.add_argument. The "choices" parameter specifies the allowable options, while setting "nargs" to "?" specifies that 1 argument should be consumed if available, otherwise none should be consumed.

    Edit: If you want to add a FORCE command with an argument and have separate help text for the ON and OFF command then you do need to use subcommands. Unfortunately there doesn't seem to be a way of specifying a default subcommand. However, you can work around the problem by checking for an empty argument list and supplying your own. Here's some sample code illustrating what I mean:

    import argparse
    import sys
    
    def print_state(ignored):
        print "Print state"
    
    def set_state(s):
        print "Setting state to " + s
    
    parser = argparse.ArgumentParser()
    subparsers = parser.add_subparsers()
    on = subparsers.add_parser('ON', help = 'On help here.')
    on.set_defaults(func = set_state, newstate = 'ON')
    off = subparsers.add_parser('OFF', help = 'Off help here.')
    off.set_defaults(func = set_state, newstate = 'OFF')
    prt = subparsers.add_parser('PRINT')
    prt.set_defaults(func = print_state, newstate = 'N/A')
    force = subparsers.add_parser('FORCE' , help = 'Force help here.')
    force.add_argument('newstate', choices = [ 'ON', 'OFF' ])
    force.set_defaults(func = set_state)
    
    if (len(sys.argv) < 2):
        args = parser.parse_args(['PRINT'])
    else:
        args = parser.parse_args(sys.argv[1:])
    
    args.func(args.newstate)
    
    0 讨论(0)
提交回复
热议问题