使用python argparse实现简单的click

python3 argparse 文档

python3 ArgParse教程

需求

实现一个类似click command的类,支持将函数作为命令注册进去,调用这个类的时候,解析命令行参数,执行对应的注册函数。

可以用装饰器的方法进行函数注册以及参数添加

类名: Entrance

方法:

  • 注册: Entrance.register
  • 参数:Entrance.option

实现

entrance.py

# encoding: utf-8
"""the only entrance for the program"""
import argparse


class EntranceConflict(Exception):
    pass


class Entrance:
    def __init__(self):
        self.parser = argparse.ArgumentParser()
        self.sub_parser = self.parser.add_subparsers()
        self.entrances = {}

    def register(self, name=None):
        """ 注册一个函数作为入口
        可以指定name,否则默认使用函数的名字
        """
        def decorator(f):
            entrance_name = name or f.__name__
            if entrance_name in self.entrances:
                raise EntranceConflict(f"entrance {entrance_name} already registered.")
            # make parser
            _parser = self.sub_parser.add_parser(name=entrance_name)
            _params = f.__entrance_params__
            for _args, _kwargs in _params:
                _parser.add_argument(*_args, **_kwargs)
            _parser.set_defaults(processor=f)
            # add parser to entrances
            self.entrances[entrance_name] = _parser

            return f

        return decorator

    def option(self, *args, **kwargs):
        """ 注册一个函数的参数
        参数格式参考 argparse.ArgumentParser.add_argument
        """
        def decorator(f):
            # set tmp attrs for f
            if not hasattr(f, '__entrance_params__'):
                f.__entrance_params__ = []
            f.__entrance_params__.append((args, kwargs))
            return f

        return decorator

    def __call__(self, *args, **kwargs):
        args = self.parser.parse_args()
        # extrac args
        params = vars(args)
        processor = params.get('processor')
        if processor:
            del params['processor']
            return processor(**params)
        else:
            print('un processable entrance: {}'.format(args))
            self.parser.print_help()


entrance = Entrance()

调用

写一个简单的调用demo

main.py

from entrance import entrance 


@entrance.register()
@entrance.option('--user-name', required=True)
@entrance.option('--age')
def add_user(user_name, age):
    print("Added user: {}, age={}".format(user_name, age))
    
    
@entrance.register()
@entrance.option('--user-id', required=True)
def del_user(user_id):
    print("deleted user whose id = {}".format(user_id))
    

if __name__ == '__main__':
    entrance()

运行

$ python main.py add_user --user-name Mike --age 19
Added user: Mike, age=19

$ python main.py del_user --user-id 10   
deleted user whose id = 10