python argparse 子命令解析(续二)

参考资料 http://docs.python.org/dev/library/argparse.html


子命令 sub-commands

如svn命令    svn checkout, svn update, svn commit

主命令svn,子命令checkout、update和commit

argparse可通过add_subparsers 以及add_parser来达到效果。

步骤1.在顶层的解析器下,先定义一个subparsers,它是一个子命令解析的对象,用来产生子命令解析器 (注意,每个解析器,只能有一个subparsers)

步骤2.在subparsers下,分别定义所需要的子解析器(一个subparsers下可以有多个parser),子解析器间是互斥的关系,一个时刻只能匹配一个。

实例1

import argparse

####example 1############
#create top-level parser
parser = argparse.ArgumentParser(prog='PROG')
parser.add_argument('--foo', action='store_true', help='foo help')
subparsers = parser.add_subparsers(help='sub-command help')

#create the parser for the 'a' command
parser_a = subparsers.add_parser('a', help='a help')
parser_a.add_argument('bar', type=int, help='bar help')

#create the parser for the 'b' command
parser_b = subparsers.add_parser('b', help='b help')
parser_b.add_argument('--baz', choices='xyz', help='baz help')

#parse some argument lists
parser.parse_args(['a','12'])
#Namespace(bar=12, foo=False)

parser.parse_args(['--foo', 'b','--baz','z'])
#Namespace(baz='Z', foo=True)

parser.parse_args(['--help'])
#usage: PROG [-h] [--foo] {a,b} ...
#
#positional arguments:
#  {a,b}       sub-command help
#    a         a help
#    b         b help
#
#optional arguments:
#  -h, --help  show this help message and exit
#  --foo       foo help

parser.parse_args(['a','-h'])
#usage: PROG a [-h] bar
#
#positional arguments:
#  bar         bar help
#
#optional arguments:
#  -h, --help  show this help message and exit

parser.parse_args(['b','-h'])
#usage: PROG b [-h] [--baz {x,y,z}]
#
#optional arguments:
#  -h, --help     show this help message and exit
#  --baz {x,y,z}  baz help


实例2

add_subparsers()可以添加title以及description的参数,还有dest参数(用来保存匹配出来的子解析器名)

此外,可以通过子命令解析器设置默认值set_defaults()来达到子解析器与功能函数对应关系

###########example 2 ################
#添加子命令函数
def foo(args):
    print (args.x * args.y)

def bar(args):
    print ('((%s))' %args.z)

#创建最上层解析器
parser = argparse.ArgumentParser()
subparsers = parser.add_subparsers(title='subcommands',
                                    description='valid subcommands',
                                    help='additional help',
                                    dest='subparser_name')

#创建子解析器 'foo'
parser_foo = subparsers.add_parser('foo')
parser_foo.add_argument('-x', type=int, default=1)
parser_foo.add_argument('y', type=float)
parser_foo.set_defaults(func=foo) #将函数foo 与子解析器foo绑定

#创建子解析器‘bar'
parser_bar = subparsers.add_parser('bar')
parser_bar.add_argument('z')
parser_bar.set_defaults(func=bar) #将函数bar与子解析器bar绑定

args = parser.parse_args('foo 1 -x 2'.split())
#Namespace(func=, subparser_name='foo', x=2, y=1.0)
args.func(args)
#2.0

args = parser.parse_args('bar xyzyz'.split())
#Namespace(func=, subparser_name='bar', z='xyzyz')
args.func(args)
#((xyzyz))

parser.parse_args(['-h'])
#usage: subparser_example.py [-h] {foo,bar} ...
#
#optional arguments:
#  -h, --help  show this help message and exit
#
#subcommands:
#  valid subcommands
#
#  {foo,bar}   additional help

注意:

子解析器下还可以定义孙子解析器


3.实际应用例子(参考openstack cinder-manage)

现在要求有一个manager, 有 db 以及host两个子命令

db命令可以有update(更新数据库) 以及sync操作(同步数据库)

host命令可以有list操作(列出host信息)

分析

db 以及host是互斥关系,因此db 和host为子命令

而db下的update 和sync命令又是互斥关系,因此它两为db的子命令

因此全局解析器分三层,顶层,子层(db、host) 以及孙子层db.sync,db.update 和host.list

import argparse

def args(*args,**kwargs):
    def _decorator(func):
        func.__dict__.setdefault('args', []).insert(0, (args,kwargs))
        return func
    return _decorator

class Dbcommands(object):
    def __init__(self):
        pass

    @args('version', nargs='?', default='versiondefault',
            help='version help')
    def sync(self, version=None):
        print "do Dbcommand.sync(version=%s)." %version

    def update(self):
        print "do Dbcommand.update()."

class Hostcommands(object):
    @args('zone', nargs='?', default="zonedefault",
            help=' zone help')
    def list(self,zone=None):
        print "do Hostcomands.list(zone=%s)." %zone

def methods_of(obj):
    result = []
    for i in dir(obj):
        if callable(getattr(obj, i)) and not i.startswith('_'):
            result.append((i, getattr(obj, i)))
    return result

def fetch_func_args(func,matchargs):
    fn_args = []
    for args,kwargs in getattr(func, 'args', []):
        arg = args[0]
        fn_args.append(getattr(matchargs, arg))

    return fn_args

CATEGORIES = {
    'db' : Dbcommands,
    'host': Hostcommands}

if __name__ == "__main__":
    top_parser = argparse.ArgumentParser(prog='top')

    subparsers = top_parser.add_subparsers()

    for category in CATEGORIES:
        command_object = CATEGORIES[category]()

        category_parser = subparsers.add_parser(category)
        category_parser.set_defaults(command_object=command_object)

        category_subparsers = category_parser.add_subparsers(dest='action')
        for (action, action_fn) in methods_of(command_object):
            parser = category_subparsers.add_parser(action)

            action_kwargs = []
            for args, kwargs in getattr(action_fn, 'args', []):
                parser.add_argument(*args, **kwargs)

            parser.set_defaults(action_fn=action_fn)
            parser.set_defaults(action_kwargs=action_kwargs)


    match_args = top_parser.parse_args('db sync 3'.split())
    print 'match_args:',match_args

    fn = match_args.action_fn
    fn_args = fetch_func_args(fn,match_args)

    #do the match func
    fn(*fn_args)

运行结果

>>match_args: Namespace(action='sync', action_fn=>, action_kwargs=[], command_object=<__main__.Dbcommands object at 0x7f67a8b1cbd0>, version='3')
do Dbcommand.sync(version=3).


你可能感兴趣的:(python argparse 子命令解析(续二))