# 项目：标准库函数
# 模块：命令行处理模块
# 作者：黄涛
# License:GPL
# Email:huangtao.sh@icloud.com
# 创建：2017-01-18 21:34
# 修改：2017-02-05 为__call__函数增加位置参数，可以支持主程序为classmethod
# 修改：2017-02-11 调整相关函数名
 
from functools import partial

__all__='command','arg'

class _Command(object):
    kwargs={}            # ArgumentParser 的参数，由command函数生成
    allow_empty=False # 是否允许参数为空，如允许则argv为None的情况下也调用执行函数
    def __init__(self,run):
        self.run=run        # 可执行函数，命令行解析成功时调用  
        self.args=[]        # 参数列表
        self.subcommands=[] # 子命令列表
        
    @classmethod
    def wrapper(cls,cmd,*args,**kw):
        def _(fn):
            if not isinstance(fn,cls):
                fn=cls(fn)
            getattr(fn,cmd)(*args,**kw)
            return fn
        return _

    def add_subcommand(self,subcommand):
        self.subcommands.append(subcommand)
        
    def add_arg(self,*args,**kw):
        self.args.append((args,kw))

    def command(self,*subcommands,allow_empty=False,**kwargs):
        self.kwargs=kwargs                  # ArgumentParser 的参数
        self.subcommands=list(subcommands)  # 子命令
        self.allow_empty=allow_empty        # 是否允许命令行为空

    def proc_args(self,parser):
        for args,kw in reversed(self.args):
            parser.add_argument(*args,**kw)

    def __call__(self,*args,argv=None):
        import sys
        from argparse import ArgumentParser
        argv=argv or sys.argv[1:]
        parser=ArgumentParser(**self.kwargs)
        self.proc_args(parser)
        parser.set_defaults(proc=self.run)
        if self.subcommands:
            subparsers=parser.add_subparsers(help='Sub command')
            for subcommand in self.subcommands:
                help=subcommand._kw.pop('description')
                if help:
                    subcommand._kw['help']=help
                sub=subparsers.add_parser(subcommand.run.__name__,
                            **subcommand.kwargs)
                subcommand.proc_args(sub)
                sub.set_defaults(proc=subcommand.run)
        if self.allow_empty or argv:
            kwargs=dict(parser.parse_args(argv)._get_kwargs())
            proc=kwargs.pop('proc',None)
            if proc:
                try:
                    proc(*args,**kwargs)
                except Exception as e:
                    print(str(e))
        else:
            parser.print_usage()

command=partial(_Command.wrapper,'command')
arg=partial(_Command.wrapper,'add_arg')

