目录
- 一、前置说明
- 1、总体目录
- 2、相关回顾
- 3、本节目标
- 二、操作步骤
- 1、项目目录
- 2、代码实现
- 3、测试代码
- 4、日志输出
- 三、后置说明
- 1、要点小结
- 2、下节预告
一、前置说明
1、总体目录
- 《 pyparamvalidate 参数校验器,从编码到发布全过程》
2、相关回顾
- pyparamvalidate 项目背景和需求分析
3、本节目标
param_validator
核心代码实现
二、操作步骤
1、项目目录
atme
:@me
用于存放临时的代码片断或其它内容。pyparamvalidate
: 新建一个与项目名称同名的package,为了方便发布至pypi
。core
: 用于存放核心代码。tests
: 用于存放测试代码。utils
: 用于存放一些工具类或方法。
2、代码实现
pyparamvalidate/core/param_validator.py
import functools
import inspect
import os.path
class ParameterValidationError(Exception):
def __init__(self, func, parameter_name, parameter_value, param_rule_description=None, exception_msg=None):
self.func_name = func.__name__
self.parameter_name = parameter_name
self.parameter_value = parameter_value
self.param_rule_description = param_rule_description
self.exception_msg = exception_msg
super().__init__(self.error_message())
def error_message(self):
return (
f"Parameter '{self.parameter_name}' in function '{self.func_name}' is invalid. "
f"\t{'Error: ' + self.exception_msg if self.exception_msg else ''}"
f"\t{'Please refer to: ' + self.param_rule_description if self.param_rule_description else ''}"
)
class ParameterValidator:
def __init__(self, param_name, param_rule_des=None):
"""
:param param_name: 参数名
:param param_rule_des: 该参数的规则描述
"""
self.param_name = param_name
self.param_rule_description = param_rule_des
# 用于收集校验器
self.validators = []
def __call__(self, func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
# 获取函数的参数和参数值
bound_args = inspect.signature(func).bind(*args, **kwargs).arguments
# 如果函数被装饰,且以关键字参数传值,则从 kwargs 中取参数值
if self.param_name in kwargs:
value = kwargs[self.param_name]
# # 如果函数被装饰,且以位置参数传值,则从 bound_args 中取参数值
elif self.param_name in bound_args:
value = bound_args.get(self.param_name)
# 如果函数没有被装饰,则直接执行返回结果
else:
return func(*args, **kwargs)
# 使用验证器进行验证
for validator, exception_msg in self.validators:
# 如果没验证通过,则抛出异常
if not validator(value):
raise ParameterValidationError(func, self.param_name, value,
self.param_rule_description, exception_msg)
# 所有参数校验通过,则执行函数返回结果
return func(*args, **kwargs)
return wrapper
def add_validator(self, validator_function, exception_msg=None):
self.validators.append((validator_function, exception_msg))
return self
def is_string(self, exception_msg=None):
return self.add_validator(lambda value: isinstance(value, str), exception_msg)
3、测试代码
pyparamvalidate/tests/test_param_validator.py
import os
import pytest
from pyparamvalidate.core.param_validator import ParameterValidator, ParameterValidationError
def test_is_string_validator_passing_01():
"""
不描述参数规则
"""
@ParameterValidator("param").is_string()
def example_function(param):
print(param)
return param
assert example_function(param="test") == "test"
with pytest.raises(ParameterValidationError) as exc_info:
example_function(param=123)
print(exc_info.value)
assert "invalid" in str(exc_info.value)
def test_is_string_validator_passing_02():
"""
在校验器中描述参数规则
"""
@ParameterValidator("param").is_string("Value must be a string")
def example_function(param):
print(param)
return param
assert example_function(param="test") == "test"
with pytest.raises(ParameterValidationError) as exc_info:
example_function(param=123)
print(exc_info.value)
assert "Value must be a string" in str(exc_info.value)
def test_is_string_validator_passing_03():
"""
在 ParameterValidator 实例化时描述参数规则
"""
@ParameterValidator("param", param_rule_des="Value must be a string").is_string()
def example_function(param):
print(param)
return param
assert example_function(param="test") == "test"
with pytest.raises(ParameterValidationError) as exc_info:
example_function(param=123)
print(exc_info.value)
assert "Value must be a string" in str(exc_info.value)
4、日志输出
执行 test
的日志如下,验证通过:
============================= test session starts =============================
collecting ... collected 3 items
test_param_validator.py::test_is_string_validator_passing_01 PASSED [ 33%]test
Parameter 'param' in function 'example_function' is invalid.
test_param_validator.py::test_is_string_validator_passing_02 PASSED [ 66%]test
Parameter 'param' in function 'example_function' is invalid. Error: Value must be a string
test_param_validator.py::test_is_string_validator_passing_03 PASSED [100%]test
Parameter 'param' in function 'example_function' is invalid. Please refer to: Value must be a string
============================== 3 passed in 0.01s ==============================
三、后置说明
1、要点小结
- 使用
__call__
方法,实现一个类装饰器; - 使用
inspect.signature(func).bind(*args, **kwargs).arguments
绑定参数名和参数值; - 如果是关键字参数,则使用
kwargs[self.param_name]
获取参数值; - 如果是位置参数,则使用
bound_args.get(self.param_name)
获取参数值; - 使用装饰器时,可以:不描述规则、在校验器中描述规则、在
ParameterValidator
实例化时描述参数规则。
2、下节预告
- param_validator 常用校验器的实现
点击返回主目录