python - 责任链模式实现(2)

pluggy 是 Python 的一个插件管理库。pytest 利用 pluggy 实现插件化,在 Flask 或 Django 等 web 框架中,可以使用 pluggy 为 Middleware 或 View 功能扩展钩子。在数据流管道中,通过 pluggy 添加钩子实现数据采集、过滤、聚合等功能可用于处理数据流的不同阶段。

1. 创建过滤器接口

python 复制代码
from pluggy import HookimplMarker
from pluggy import HookspecMarker
from pluggy import PluginManager

hookspec = HookspecMarker("example.filter")
hookimpl = HookimplMarker("example.filter")


class FilterSpec:
    """拦截器接口 ."""

    @hookspec
    def process_request(self, request):
        pass

    @hookspec
    def process_response(self, request, response):
        pass

    @hookspec
    def process_view(self, request, view_func, view_args, view_kwargs):
        pass

FilterSpec 定义了一个插件的接口,其中每个用 @hookspec 装饰的函数可以理解为 hook 钩子 的接口声明。

2. 创建插件即过滤器的实现

python 复制代码
class FilterPlugin1:
    @hookimpl
    def process_request(self, request):
        request.data["result"].append("FilterPlugin1")

    @hookimpl(wrapper=True)
    def process_response(self, request, response):
        response.data["data"].append("FilterPlugin1 before yield")
        # 接受插件 FilterPlugin2 的返回结果
        results = yield
        results.append("FilterPlugin1")
        response.data["data"].append("FilterPlugin1 after yield")
        return results

class FilterPlugin2:
    @hookimpl
    def process_request(self, request):
        request.data["result"].append("FilterPlugin2")

    @hookimpl(wrapper=True)
    def process_response(self, request, response):
        response.data["data"].append("FilterPlugin2 before yield")
        # 接受插件 FilterPlugin3 的返回结果
        results = yield
        results.append("FilterPlugin2")
        response.data["data"].append("FilterPlugin2 after yield")
        return results
        
class FilterPlugin3:
    @hookimpl
    def process_request(self, request):
        request.data["result"].append("FilterPlugin3")

    @hookimpl(wrapper=True)
    def process_response(self, request, response):
        response.data["data"].append("FilterPlugin3 before yield")
        # 接受其他插件的执行结果
        results = yield
        results.append("FilterPlugin3")
        response.data["data"].append("FilterPlugin3 after yield")
        return results

@hookimpl 装饰器实现了 hook 钩子 的业务逻辑。

wrapper=True 可以使得 hook 可以接收其它插件的执行结果。

3. 注册插件

python 复制代码
# 初始化 PluginManager
pm = PluginManager("example.filter")

# 登记 hook 集合(hook函数声明)
pm.add_hookspecs(FilterSpec)

# 注册插件(hook函数实现)
pm.register(FilterPlugin3()) # 第一个注册的插件最后执行
pm.register(FilterPlugin2())
pm.register(FilterPlugin1()) # 最后注册的插件最先执行

class Request:
    def __init__(self):
        self.data = {"result": []}


class Response:
    def __init__(self):
        self.data = {
            "code": 0,
            "data": [],
            "message": "",
        }

4. 单元测试

4.1 模拟请求中间件实现

python 复制代码
def test_hook_request():
    # 调用两个插件类中的同名hook函数, 后注册的插件中的函数会先被调用
    request = Request()
    results = pm.hook.process_request(request=request)
    assert results == []  # 钩子实现返回 None
    assert request.data == {'result': ['FilterPlugin1', 'FilterPlugin2', 'FilterPlugin3']}

4.2 模拟响应中间件实现

python 复制代码
def test_hook_response():
    request = Request()
    response = Response()
    results = pm.hook.process_response(request=request, response=response)
    assert results == ['FilterPlugin3', 'FilterPlugin2', 'FilterPlugin1']
    assert response.data == {
        'code': 0,
        'data': [
            'FilterPlugin1 before yield',
            'FilterPlugin2 before yield',
            'FilterPlugin3 before yield',
            'FilterPlugin3 after yield',
            'FilterPlugin2 after yield',
            'FilterPlugin1 after yield',
        ],
        'message': '',
    }
相关推荐
特立独行的猫A4 小时前
Ionic + 仓颉语言的 cj-tauri 开发框架实战:开发精美移动端 APP
java·后端·python
高洁014 小时前
数据同化与动态状态估计:让孪生持续贴合现实
人工智能·python·深度学习·transformer·迁移学习
通信仿真爱好者5 小时前
第【139】期--基于深度学习的OFDM信道估计方法研究--python完整代码
python·深度学习·ofdm·信道估计
威哥爱编程5 小时前
模拟面试智能体开发实战 07:答完一题就点评一题,结构化输出给分建议,让每道题都变成复盘
python·agent·harmonyos
cpolar技术支持5 小时前
中文文件名排序跟拼音对不上:os.listdir 返回的是文件系统顺序,三个中文名按字节排结果正好相反
python·文件系统·cpolar·编码·排序
Thanks_ks5 小时前
【第 008 讲】在终端中运行 Python 程序:交互模式与脚本模式 | 绝对路径与相对路径 | 以模块方式运行 | 常见报错的识别与排查
python·异常处理·终端·脚本·命令行·cmd·python 解释器
威哥爱编程5 小时前
模拟面试智能体开发实战 08:总评报告先过三道闸,防泄题防夸大防错建议,才敢发到候选人手上
python·agent·harmonyos
郝学胜-神的一滴5 小时前
LangChain Skill 实现(渐进披露 On-Demand Skill,LangChain 官方 Deep Agent Skill 模式)
开发语言·python·langchain·numpy
投图匠16 小时前
十种不同风格的 CTP接口 框架
开发语言·python
2601_9679801117 小时前
深入理解词向量算法及其在Python中的实践
python·word2vec·glove·fasttext·词向量