将 validator 校验器从 ParameterValidator 中抽离出来

news2024/11/20 2:25:35

目录

  • 一、前置说明
    • 1、总体目录
    • 2、相关回顾
    • 3、本节目标
  • 二、操作步骤
    • 1、项目目录
    • 2、代码实现
    • 3、测试代码
    • 4、日志输出
  • 三、后置说明
    • 1、要点小结
    • 2、下节准备

一、前置说明

1、总体目录

  • 《 pyparamvalidate 参数校验器,从编码到发布全过程》

2、相关回顾

  • pyparamvalidate 重构背景和需求分析

3、本节目标

  • validator 校验器从 ParameterValidator 中抽离出来

二、操作步骤

1、项目目录

  • validator.py : 用于存放抽离出来的 validator 校验函数的代码。
  • test_validator.py :存放测试 validator 校验函数的代码。
  • __init__.py : 将 pyparamvalidate 转为 pythonpackage 包,用于统一管理 import

2、代码实现

pyparamvalidate/core/validator.py

import functools
import inspect
import os

from pyparamvalidate import DictUtility


def raise_exception(func):
    @functools.wraps(func)
    def wrapper(*args, **kwargs):
        bound_args = inspect.signature(func).bind(*args, **kwargs).arguments

        validate_field = kwargs.get('validate_field', None) or bound_args.get('validate_field', None)
        exception_msg = kwargs.get('exception_msg', None) or bound_args.get('exception_msg', None)

        result = func(*args, **kwargs)
        if not result and exception_msg is not None:
            exception_msg = f"'{validate_field}' value error: {exception_msg}" if validate_field else f"{exception_msg}"
            raise ValueError(exception_msg)
        return result

    return wrapper


@raise_exception
def is_string(value, validate_field=None, exception_msg=None):
    return isinstance(value, str)


@raise_exception
def is_string(value, validate_field=None, exception_msg=None):
    return isinstance(value, str)


@raise_exception
def is_int(value, validate_field=None, exception_msg=None):
    return isinstance(value, int)


@raise_exception
def is_positive(value, validate_field=None, exception_msg=None):
    return value > 0


@raise_exception
def is_float(value, validate_field=None, exception_msg=None):
    return isinstance(value, float)


@raise_exception
def is_list(value, validate_field=None, exception_msg=None):
    return isinstance(value, list)


@raise_exception
def is_dict(value, validate_field=None, exception_msg=None):
    return isinstance(value, dict)


@raise_exception
def is_set(value, validate_field=None, exception_msg=None):
    return isinstance(value, set)


@raise_exception
def is_tuple(value, validate_field=None, exception_msg=None):
    return isinstance(value, tuple)


@raise_exception
def is_not_none(value, validate_field=None, exception_msg=None):
    return value is not None


@raise_exception
def is_not_empty(value, validate_field=None, exception_msg=None):
    return bool(value)


@raise_exception
def is_allowed_value(value, allowed_values, validate_field=None, exception_msg=None):
    return value in allowed_values


@raise_exception
def max_length(value, max_length, validate_field=None, exception_msg=None):
    return len(value) <= max_length


@raise_exception
def min_length(value, min_length, validate_field=None, exception_msg=None):
    return len(value) >= min_length


@raise_exception
def is_substring(sub_string, super_string, validate_field=None, exception_msg=None):
    return sub_string in super_string


@raise_exception
def is_subset(subset, superset, validate_field=None, exception_msg=None):
    return subset.issubset(superset)


@raise_exception
def is_sublist(sublist, superlist, validate_field=None, exception_msg=None):
    return set(sublist).issubset(set(superlist))


@raise_exception
def contains_substring(superstring, substring, validate_field=None, exception_msg=None):
    return substring in superstring


@raise_exception
def contains_subset(superset, subset, validate_field=None, exception_msg=None):
    return subset.issubset(superset)


@raise_exception
def contains_sublist(superlist, sublist, validate_field=None, exception_msg=None):
    return set(sublist).issubset(set(superlist))


@raise_exception
def is_file_suffix(path, file_suffix, validate_field=None, exception_msg=None):
    return path.endswith(file_suffix)


@raise_exception
def is_file(path, validate_field=None, exception_msg=None):
    return os.path.isfile(path)


@raise_exception
def is_dir(path, validate_field=None, exception_msg=None):
    return os.path.isdir(path)


@raise_exception
def is_similar_dict(target_dict, reference_dict, validate_field=None, exception_msg=None, ignore_keys_whitespace=True):
    dict_util = DictUtility()
    return dict_util.is_similar_dict(target_dict, reference_dict, ignore_keys_whitespace)


@raise_exception
def is_method(value, validate_field=None, exception_msg=None):
    return callable(value)


3、测试代码

pyparamvalidate/tests/test_validator.py

import pytest

from atme.demo_v2.validator_v1 import *


def test_is_string():
    assert is_string(value="test", validate_field='value', exception_msg='value must be string')

    with pytest.raises(ValueError) as exc_info:
        is_string(value=123, validate_field='value', exception_msg='value must be string')
    assert "value must be string" in str(exc_info.value)


def test_is_int():
    assert is_int(value=42, validate_field='value', exception_msg='value must be integer')

    with pytest.raises(ValueError) as exc_info:
        is_int(value="test", validate_field='value', exception_msg='value must be integer')
    assert "value must be integer" in str(exc_info.value)


def test_is_positive():
    assert is_positive(value=42, validate_field='value', exception_msg='value must be positive')

    with pytest.raises(ValueError) as exc_info:
        is_positive(value=-1, validate_field='value', exception_msg='value must be positive')
    assert "value must be positive" in str(exc_info.value)


def test_is_float():
    assert is_float(value=3.14, validate_field='value', exception_msg='value must be float')

    with pytest.raises(ValueError) as exc_info:
        is_float(value="test", validate_field='value', exception_msg='value must be float')
    assert "value must be float" in str(exc_info.value)


def test_is_list():
    assert is_list(value=[1, 2, 3], validate_field='value', exception_msg='value must be list')

    with pytest.raises(ValueError) as exc_info:
        is_list(value="test", validate_field='value', exception_msg='value must be list')
    assert "value must be list" in str(exc_info.value)

def test_is_dict():
    assert is_dict(value={"key": "value"}, validate_field='value', exception_msg='value must be dict')

    with pytest.raises(ValueError) as exc_info:
        is_dict(value=[1, 2, 3], validate_field='value', exception_msg='value must be dict')
    assert "value must be dict" in str(exc_info.value)


def test_is_set():
    assert is_set(value={1, 2, 3}, validate_field='value', exception_msg='value must be set')

    with pytest.raises(ValueError) as exc_info:
        is_set(value=[1, 2, 3], validate_field='value', exception_msg='value must be set')
    assert "value must be set" in str(exc_info.value)


def test_is_tuple():
    assert is_tuple(value=(1, 2, 3), validate_field='value', exception_msg='value must be tuple')

    with pytest.raises(ValueError) as exc_info:
        is_tuple(value=[1, 2, 3], validate_field='value', exception_msg='value must be tuple')
    assert "value must be tuple" in str(exc_info.value)


def test_is_not_none():
    assert is_not_none(value="test", validate_field='value', exception_msg='value must not be None')

    with pytest.raises(ValueError) as exc_info:
        is_not_none(value=None, validate_field='value', exception_msg='value must not be None')
    assert "value must not be None" in str(exc_info.value)


def test_is_not_empty():
    assert is_not_empty(value="test", validate_field='value', exception_msg='value must not be empty')

    with pytest.raises(ValueError) as exc_info:
        is_not_empty(value="", validate_field='value', exception_msg='value must not be empty')
    assert "value must not be empty" in str(exc_info.value)


def test_is_allowed_value():
    assert is_allowed_value(value=3, allowed_values=[1, 2, 3, 4, 5], validate_field='value', exception_msg='value must be in allowed_values')

    with pytest.raises(ValueError) as exc_info:
        is_allowed_value(value=6, allowed_values=[1, 2, 3, 4, 5], validate_field='value', exception_msg='value must be in allowed_values')
    assert "value must be in allowed_values" in str(exc_info.value)


def test_max_length():
    assert max_length(value="test", max_length=5, validate_field='value', exception_msg='value length must be less than or equal to 5')

    with pytest.raises(ValueError) as exc_info:
        max_length(value="test", max_length=3, validate_field='value', exception_msg='value length must be less than or equal to 3')
    assert "value length must be less than or equal to 3" in str(exc_info.value)


def test_min_length():
    assert min_length(value="test", min_length=3, validate_field='value', exception_msg='value length must be greater than or equal to 3')

    with pytest.raises(ValueError) as exc_info:
        min_length(value="test", min_length=5, validate_field='value', exception_msg='value length must be greater than or equal to 5')
    assert "value length must be greater than or equal to 5" in str(exc_info.value)


def test_is_substring():
    assert is_substring(sub_string="st", super_string="test", validate_field='sub_string', exception_msg='sub_string must be a substring of super_string')

    with pytest.raises(ValueError) as exc_info:
        is_substring(sub_string="abc", super_string="test", validate_field='sub_string', exception_msg='sub_string must be a substring of super_string')
    assert "sub_string must be a substring of super_string" in str(exc_info.value)


def test_is_subset():
    assert is_subset(subset={1, 2}, superset={1, 2, 3, 4}, validate_field='subset', exception_msg='subset must be a subset of superset')

    with pytest.raises(ValueError) as exc_info:
        is_subset(subset={5, 6}, superset={1, 2, 3, 4}, validate_field='subset', exception_msg='subset must be a subset of superset')
    assert "subset must be a subset of superset" in str(exc_info.value)


def test_is_sublist():
    assert is_sublist(sublist=[1, 2], superlist=[1, 2, 3, 4], validate_field='sublist', exception_msg='sublist must be a sublist of superlist')

    with pytest.raises(ValueError) as exc_info:
        is_sublist(sublist=[5, 6], superlist=[1, 2, 3, 4], validate_field='sublist', exception_msg='sublist must be a sublist of superlist')
    assert "sublist must be a sublist of superlist" in str(exc_info.value)


def test_contains_substring():
    assert contains_substring(superstring="test", substring="es", validate_field='superstring', exception_msg='superstring must contain substring')

    with pytest.raises(ValueError) as exc_info:
        contains_substring(superstring="test", substring="abc", validate_field='superstring', exception_msg='superstring must contain substring')
    assert "superstring must contain substring" in str(exc_info.value)


def test_contains_subset():
    assert contains_subset(superset={1, 2, 3, 4}, subset={1, 2}, validate_field='superset', exception_msg='superset must contain subset')

    with pytest.raises(ValueError) as exc_info:
        contains_subset(superset={1, 2, 3, 4}, subset={5, 6}, validate_field='superset', exception_msg='superset must contain subset')
    assert "superset must contain subset" in str(exc_info.value)


def test_contains_sublist():
    assert contains_sublist(superlist=[1, 2, 3, 4], sublist=[1, 2], validate_field='superlist', exception_msg='superlist must contain sublist')

    with pytest.raises(ValueError) as exc_info:
        contains_sublist(superlist=[1, 2, 3, 4], sublist=[5, 6], validate_field='superlist', exception_msg='superlist must contain sublist')
    assert "superlist must contain sublist" in str(exc_info.value)


def test_is_file_suffix():
    assert is_file_suffix(path="example.txt", file_suffix=".txt", validate_field='path', exception_msg='path must have the specified file suffix')

    with pytest.raises(ValueError) as exc_info:
        is_file_suffix(path="example.txt", file_suffix=".csv", validate_field='path', exception_msg='path must have the specified file suffix')
    assert "path must have the specified file suffix" in str(exc_info.value)


def test_is_file():
    assert is_file(path=__file__, validate_field='path', exception_msg='path must be an existing file')

    with pytest.raises(ValueError) as exc_info:
        is_file(path="nonexistent_file.txt", validate_field='path', exception_msg='path must be an existing file')
    assert "path must be an existing file" in str(exc_info.value)


def test_is_dir():
    assert is_dir(path=os.path.dirname(__file__), validate_field='path', exception_msg='path must be an existing directory')

    with pytest.raises(ValueError) as exc_info:
        is_dir(path="nonexistent_directory", validate_field='path', exception_msg='path must be an existing directory')
    assert "path must be an existing directory" in str(exc_info.value)


def test_is_similar_dict():
    dict1 = {"key1": "value1", "key2": "value2"}
    dict2 = {"key1": "value2", "key2": "value3"}
    assert is_similar_dict(target_dict=dict1, reference_dict=dict2, validate_field='target_dict', exception_msg='target_dict must be similar to reference_dict')

    dict3 = {"key2": "value1", "key3": "value3"}
    with pytest.raises(ValueError) as exc_info:
        is_similar_dict(target_dict=dict1, reference_dict=dict3, validate_field='target_dict', exception_msg='target_dict must be similar to reference_dict')
    assert "target_dict must be similar to reference_dict" in str(exc_info.value)


def test_is_method():
    assert is_method(value=print, validate_field='value', exception_msg='value must be a callable method')

    with pytest.raises(ValueError) as exc_info:
        is_method(value="test", validate_field='value', exception_msg='value must be a callable method')
    assert "value must be a callable method" in str(exc_info.value)


4、日志输出

执行 test 的日志如下,验证通过:

============================= test session starts =============================
collecting ... collected 24 items

test_validator.py::test_is_string PASSED                                 [  4%]
test_validator.py::test_is_int PASSED                                    [  8%]
test_validator.py::test_is_positive PASSED                               [ 12%]
test_validator.py::test_is_float PASSED                                  [ 16%]
test_validator.py::test_is_list PASSED                                   [ 20%]
test_validator.py::test_is_dict PASSED                                   [ 25%]
test_validator.py::test_is_set PASSED                                    [ 29%]
test_validator.py::test_is_tuple PASSED                                  [ 33%]
test_validator.py::test_is_not_none PASSED                               [ 37%]
test_validator.py::test_is_not_empty PASSED                              [ 41%]
test_validator.py::test_is_allowed_value PASSED                          [ 45%]
test_validator.py::test_max_length PASSED                                [ 50%]
test_validator.py::test_min_length PASSED                                [ 54%]
test_validator.py::test_is_substring PASSED                              [ 58%]
test_validator.py::test_is_subset PASSED                                 [ 62%]
test_validator.py::test_is_sublist PASSED                                [ 66%]
test_validator.py::test_contains_substring PASSED                        [ 70%]
test_validator.py::test_contains_subset PASSED                           [ 75%]
test_validator.py::test_contains_sublist PASSED                          [ 79%]
test_validator.py::test_is_file_suffix PASSED                            [ 83%]
test_validator.py::test_is_file PASSED                                   [ 87%]
test_validator.py::test_is_dir PASSED                                    [ 91%]
test_validator.py::test_is_similar_dict PASSED                           [ 95%]
test_validator.py::test_is_method PASSED                                 [100%]

============================= 24 passed in 0.04s ==============================

三、后置说明

1、要点小结

  • 校验函数中的关键字参数 validate_field=None, exception_msg=None 虽然没有被直接调用,但它实际在 raise_exception 装饰器中被隐式调用了。
  • 显示优于隐式,不建议用 **kwargs 代替 validate_field=None, exception_msg=None,方便在编辑器 pycharm 中为调用方智能提示参数。
  • 如果校验函数中有多个参数,永远将被校验的参数放在最前面、参照参数放在后面。
    @raise_exception
    def is_sublist(sublist, superlist, validate_field=None, exception_msg=None):
        return set(sublist).issubset(set(superlist))
    
    
    @raise_exception
    def contains_substring(superstring, substring, validate_field=None, exception_msg=None):
        return substring in superstring
    
    @raise_exception
    def is_similar_dict(target_dict, reference_dict, validate_field=None, exception_msg=None, ignore_keys_whitespace=True):
        dict_util = DictUtility()
        return dict_util.is_similar_dict(target_dict, reference_dict, ignore_keys_whitespace)
    
  • 在每一个校验函数上都添加 @raise_exception 装饰器,显得有点累赘,可以继续优化。

2、下节准备

  • 使用 RaiseExceptionMeta 元类隐式装饰 Validator 类中的所有校验函数。

点击返回主目录

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1358514.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

科研上新 | 第6期:优化LLM数学推理;深度学习建模基因表达调控;基于深度学习的近实时海洋碳汇估算

编者按&#xff1a;欢迎阅读“科研上新”栏目&#xff01;“科研上新”汇聚了微软亚洲研究院最新的创新成果与科研动态。在这里&#xff0c;你可以快速浏览研究院的亮点资讯&#xff0c;保持对前沿领域的敏锐嗅觉&#xff0c;同时也能找到先进实用的开源工具。 本期内容速览 …

【深度学习:SENet】信道注意力和挤压激励网络(SENet):图像识别的新突破

【深度学习&#xff1a;SENet】信道注意力和挤压激励网络&#xff08;SENet&#xff09;&#xff1a;图像识别的新突破 为什么有效如何实现工作原理应用案例 挤压和激励网络&#xff08;SENets&#xff09;为卷积神经网络&#xff08;CNN&#xff09;引入了一个新的构建模块&am…

centos 7.9安装RocketMQ4.6.1版本

1.先下载二进制文件 下载 | RocketMQ 2.下载后&#xff0c;进行解压 unzip rocketmq-all-4.6.1-bin-release.zip 3.修改JVM配置 进到/datadrive/rocketmq-all-4.6.1-bin-release/bin下编辑runserver.sh 与 runbroker.sh文件 根据个人虚拟机大小进行修改 vi runserver.sh J…

这个应该是全网最全的接口测试工具之postman

概念 接口测试是什么&#xff1f; 百度百科给出的解释是&#xff1a; 接口测试是测试系统组件间接口的一种测试。接口测试主要用于检测外部系统与系统之间以及内部各个子系统之间的交互点。测试的重点是要检查数据的交换&#xff0c;传递和控制管理过程&#xff0c;以及系统间…

下载知虾数据分析软件:优化店铺运营、提高转化率的利器

在如今竞争激烈的电商市场中&#xff0c;对销售数据、流量以及买家行为等关键指标的监控和分析至关重要。Shopee平台为卖家提供了一个内置的在线数据分析工具——“Shopee Analytics”&#xff08;知虾分析&#xff09;&#xff0c;让卖家能够轻松实现对店铺运营的优化、提高转…

40 个简单又有效的 Linux Shell 脚本示例

【收藏】Linux系统常用命令速查手册&#xff08;附PDF下载方式&#xff09;_linux命令大全详解pdf-CSDN博客 历史上&#xff0c;shell 一直是类 Unix 系统的本地命令行解释器。它已被证明是 Unix 的主要功能之一&#xff0c;并发展成为一个全新的主题。Linux 提供了各种功能强大…

LeetCode 20.有效括号 详解(c语言实现) (⌯꒪꒫꒪)੭

题目详情&#xff1a; 思路&#xff1a;Step1:如果是左括号&#xff0c;入栈 Step2:如果是右括号&#xff0c;就出栈顶的元素与右括号进行比对&#xff0c;如果匹配&#xff0c;继续&#xff0c;直到都匹配成功结束。否则退出&#xff0c;直接返回false. 栗子&#xff1a;1. {…

C#之反编译之路(一)

本文将介绍微软反编译神器dnSpy的使用方法 c#反编译之路(一) dnSpy.exe区分64位和32位,所以32位的程序,就用32位的反编译工具打开,64位的程序,就用64位的反编译工具打开(个人觉得32位的程序偏多,如果不知道是32位还是64位,就先用32位的打开试试) 目前只接触到wpf和winform的桌…

Redis (三)

1、redis复制 简单的概括就是主从复制&#xff0c;master以写为主&#xff0c;Slave以读为主&#xff0c;当master数据发生变化的时候&#xff0c;自动将更新的数据异步同步到其他的slave是数据库。 使用这种机制的话&#xff0c;可以做到读写分离&#xff0c;可以减轻主机负担…

DDoS攻击的多种方式

DDOS攻击指分布式拒绝服务攻击&#xff0c;即处于不同位置的多个攻击者同时向一个或数个目标发动攻击&#xff0c;或者一个攻击者控制了位于不同位置的多台机器并利用这些机器对受害者同时实施攻击。由于攻击的发出点是分布在不同地方的&#xff0c;这类攻击称为分布式拒绝服务…

大数据毕设分享 flink大数据淘宝用户行为数据实时分析与可视化

文章目录 0 前言1、环境准备1.1 flink 下载相关 jar 包1.2 生成 kafka 数据1.3 开发前的三个小 tip 2、flink-sql 客户端编写运行 sql2.1 创建 kafka 数据源表2.2 指标统计&#xff1a;每小时成交量2.2.1 创建 es 结果表&#xff0c; 存放每小时的成交量2.2.2 执行 sql &#x…

提升网络安全重要要素IP地址

在数字化时代&#xff0c;网络安全已经成为人们关注的焦点。本文将深入探讨网络安全与IP地址之间的紧密联系&#xff0c;以及IP地址在构建数字世界的前沿堡垒中的关键作用。 网络安全是当今数字社会中不可忽视的挑战之一。而IP地址&#xff0c;作为互联网通信的基础协议&#…

震惊!原来这就是JavaScript闭包的秘密

&#x1f4e2; 鸿蒙专栏&#xff1a;想学鸿蒙的&#xff0c;冲 &#x1f4e2; C语言专栏&#xff1a;想学C语言的&#xff0c;冲 &#x1f4e2; VUE专栏&#xff1a;想学VUE的&#xff0c;冲这里 &#x1f4e2; CSS专栏&#xff1a;想学CSS的&#xff0c;冲这里 &#x1f4…

第一届能源电子产业创新大赛太阳能光伏赛道决赛及颁奖仪式在宜宾成功举办

在工业和信息化部电子信息司指导下&#xff0c;由工业和信息化部产业发展促进中心和宜宾市人民政府主办&#xff0c;宜宾市经济和信息化局、宜宾高新技术产业园区管理委员会承办的第一届能源电子产业创新大赛太阳能光伏赛道决赛及颁奖仪式于2024年1月3日-5日在宜宾市成功举办。…

DDIA 第十一章:流处理

本文是《数据密集型应用系统设计》&#xff08;DDIA&#xff09;的读书笔记&#xff0c;一共十二章&#xff0c;我已经全部阅读并且整理完毕。 采用一问一答的形式&#xff0c;并且用列表形式整理了原文。 笔记的内容大概是原文的 1/5 ~ 1/3&#xff0c;所以你如果没有很多时间…

2023年后,AI 还有什么研究方向有前景?

什么是AI ​ AI代表人工智能&#xff0c;它是指通过计算机科学技术使机器能够执行需要智力的任务的一种技术。这些任务包括学习、推理、问题解决和感知等&#xff0c;通常是人类智能的表现。人工智能的目标是使计算机系统能够执行需要人类智力的任务&#xff0c;而不需要人类的…

C语言实用第三方库Melon开箱即用之多线程模型

在之前的文章中&#xff08;开发利器——C 语言必备实用第三方库&#xff09;&#xff0c;笔者介绍了一款Linux/UNIX下C语言库Melon的基本功能&#xff0c;并给出了一个简单的多进程开箱即用的例子。 本文将给大家介绍Melon中多线程的使用方法。 在Melon中有三种多线程模式&a…

点对点SDWAN组网:通过专线连接实现企业分支互联

点对点SDWAN是一种通过软件定义网络技术将企业分支互联的组网解决方案。在点对点SDWAN中&#xff0c;企业分支通过专线连接实现互联&#xff0c;以满足对网络性能和可靠性的要求。 传统的WAN架构通常使用MPLS&#xff08;多协议标签交换&#xff09;技术来实现企业分支的互联。…

TransmittableThreadLocal使用踩坑

背景&#xff1a;为了获取相关字段方便&#xff0c;项目里使用了TransmittableThreadLocal上下文&#xff0c;在异步逻辑中get值时发现并非当前请求的值&#xff0c;且是偶发状况(并发问题)。 发现&#xff1a;TransmittableThreadLocal是阿里开源的可以实现父子线程值传递的工…

鸿蒙系列--动态共享包的依赖与使用

一、前言 HarmonyOS的共享包相当于Android的Library&#xff0c;在HarmonyOS中&#xff0c;给开发者提供了两种共享包&#xff0c;HAR&#xff08;Harmony Archive&#xff09;静态共享包&#xff0c;和HSP&#xff08;Harmony Shared Package&#xff09;动态共享包 区别&…