Python 参数检查模块: paramscheck

来源:互联网 发布:上海移动网络怎么样 编辑:程序博客网 时间:2024/04/29 22:14

同事: 老王, 我调你的接口为什么老是报错啊, 快过来看看
我: 不可能啊, 我测试过好多遍了
… 遂屁颠屁颠过去 …
… 一分钟后 …
… 半小时后 …
… 一小时后 …
我: 小明啊, 我不是在文档里写了吗, 参数 id 你 TM 给我传数字啊, 你传个字符串……

paramscheck 登场

为了应付这种情况, paramscheck登场, paramscheck 模块是一个面向切面的, 能执行复杂参数检测任务的参数检测工具

安装

pip install paramscheck

主页

github: https://github.com/Mohanson/paramscheck
pypi: https://pypi.python.org/pypi/paramscheck/

介绍

from paramscheck import paramscheck@paramscheck(id=F(int, '_ > 10000'))def login(id):    print(id)login(10500)  # 10500 login('10500')  # paramscheck.error.ErrorParamsCheck: params "id" should be: type <class 'int'> & _ > 10000: login('1')  # # paramscheck.error.ErrorParamsCheck: params "id" should be: type <class 'int'> & _ > 10000:

更多例子

@paramscheck(int) :     # first params must be int@paramscheck(int, b=str) :     # first params must be int, and b must be str@paramscheck('_ < 10') :     # first params must meet *lambda _: _ < 10*@paramscheck(lambda _: _ < 10) :     # first params must meet *lambda _: _ < 10*@paramscheck('/^\d+$/') :     # first params must be str and meet the regular@paramscheck(F(int)) :     # first params must be int@paramscheck(F(int, '_ < 10')) :     # first params must be int and less than 10@paramscheck(F(int) & F('_ < 10') :     # first params must be int and less than 10@paramscheck(F('_ < 10') | F('_ > 10')) :     # first params must less than 10 or bigger than 10@paramscheck(F(str, '_.startswith("s")')) :     # first params must be str, and startswith "s"from fn import _@paramscheck(_ < 10) :    # first params must be less than 10@paramscheck(F(_ < 10, int)) :    # first params must be less than 10 and be int

代码解析

# F 对象, 可以理解为参数检查 "条件"class F:    def __init__(self, *conditions, des=None):        for index, condition in enumerate(conditions):            if index == 0:                self.fun, self.des = self._to_condition_func(condition)            else:                current_fun, current_des = self._to_condition_func(condition)                tmp = copy.deepcopy(self.fun)                self.fun = lambda x: tmp(x) and current_fun(x)                self.des += ' & ' + current_des        self.des = des if des is not None else self.des    def __and__(self, other):        def paramscheckfunc(x):            if not self.fun(x) or not other.fun(x):                return False            return True        return F(paramscheckfunc, des=self.des + ' & ' + other.des)    def __or__(self, other):        def paramscheckfunc(x):            if self.fun(x) or other.fun(x):                return True            return False        return F(paramscheckfunc, des=self.des + ' | ' + other.des)    def __call__(self, args):        return self.fun(args)    def __str__(self):        return self.des    def _to_condition_func(self, condition):        """        if condition is a F object, return itself        if condition is a Class Type, return isinstance()        if condition is a function, return function()        if condition is a str, is str startswith and endwith '/', return re.match()                               else: return lambda _: str        """        if isinstance(condition, F):            fun = condition.fun            des = '%s' % condition.des        elif inspect.isclass(condition):            fun = lambda x: isinstance(x, condition)            des = 'type %s' % str(condition)        elif _is_function(condition):            fun = condition            des = '%s(_)' % condition.__name__        elif isinstance(condition, str):            if condition[0] == condition[-1] == '/':                fun = lambda x: re.match(condition[1:-1], x) is not None                des = 're.match(%s)' % condition[1:-1]            else:                fun = lambda _: eval(condition)                des = condition        else:            raise ErrorParamsCheck('paramscheck params error!')        return fun, des_is_function = lambda x: hasattr(x, '__call__')#  装饰器def paramscheck(*aargs, **kkwargs):    aargs = list(map(F, aargs))    kkwargs = dict([(kkwarg, F(kkwargs[kkwarg])) for kkwarg in kkwargs])    def generator(func):        params_func_dict = _params_to_paramsdict(func, aargs, kkwargs, False)        @functools.wraps(func)        def wrapper(*args, **kwargs):            paramsdict = _params_to_paramsdict(func, args, kwargs, True)            for param in params_func_dict:                fun = params_func_dict[param]                if param in paramsdict and fun(paramsdict[param]) is False:                    raise ErrorParamsCheck('params "%s" should be: %s: ' % (param, fun))            return func(*args, **kwargs)        return wrapper    return generator# 获取函数调用时的所有参数, 并转换为字典形式, 使参数和参数检测函数一一配对def _params_to_paramsdict(func, args, kwargs, is_default):    """    trans func params to dict(key, value) type    """    result = {}    iargs, ivarargs, ikeywords, defaults = inspect.getargspec(func)[:4]    # set default key/value in paramsdict if is_default    if defaults and is_default:        for index, default in enumerate(defaults[::-1]):            result[iargs[-(index + 1)]] = default    # set args value in paramsdict    for index, arg in enumerate(args):        result[iargs[index]] = args[index]    # set kwargs key/value in paramsdict    result.update(kwargs)    return result

Thanks

God and everyone

0 0