Python 插件式程序设计与开发实践总结

开发环境

win 10

python 3.6.5

代码结构

需求描述

如上,以user.py为程序入口脚本,运行该脚本时,需要创建一个user类对象,执行一系列动作(包含一系列动作的列表)。程序执行动作前,要求先获取动作名称,根据该名称,执行不同的操作。这些操作,对应不同的类函数。

实现思路

大致实现思路就是,把user对象需要运行的类函数(使用@classmethod修饰的函数,可不用创建对象进行调用),当作插件函数,并设置为user的属性,这样程序运行时,可通过该属性来调用对应的类函数。这里的问题是,程序怎么知道执行哪个类函数呢?到目前为止,程序只能根据动作名称来判断待执行的操作,所以,需要建立动作名称和类函数的映射关系。

怎么建立动作名称和类函数的映射关系呢?这里用到了装饰器,新建一个装饰器类ActionDecorator,为该类设置一个字典类型的类属性ACTION_FUNC_CLASS_MODULE_MAP,用这个类来存放动作名称和类函数的映射关系。我们把需要当作插件函数的类函数都用该装饰器进行修饰。

这里,笔者发现一个特性,就是对应模块被导入时,对应模块,对应类函数如果使用了装饰器,该装饰器函数会被执行。同时,笔者还发现另外一个特性,

首次对某个包执行import操作时,该包下面的__init__.py文件将优先被执行。基于这两个特性,我们把装饰器放在用于管理插件类函数的外围软件包下(例中的components包),同时,在该外围软件包下的__init__.py中加入动态加载插件模块的代码:遍历外围软件包下的所有非__init__.py文件,并且动态加载改模块。这样,当在user.py入口文件中,执行from components.decoraters.action_decorater import ActionDecorator时,会自动执行components/__init__.py文件,动态加载所有插件模块,并且自动触发装饰器的执行,装饰器方法执行,会自动根据提供的方法参数建立动作名称和类函数的映射关系。

然后,在初始化user对象时,给该对象动态设置属性,属性名称设置为动作名称,属性值设置为类方法,这样,执行动作时,就可以根据动作名称调用对应的类方法了。

代码实现

action_decorate.py

#!/usr/bin/env python# -*- coding:utf-8 -*-'''@CreateTime: 2020/12/09 14:58@Author : shouke'''class ActionDecorator(object):    '''    action 装饰器    '''    ACTION_FUNC_CLASS_MODULE_MAP = {}    @classmethod    def action_register(cls, action, class_name, function_name, module_path):        def wrapper(func):            cls.ACTION_FUNC_CLASS_MODULE_MAP.update({action: {'class_name':class_name, 'function_name':function_name, 'module_path':module_path}})            return func        return wrapper

components/__init__.py

#!/usr/bin/env python# -*- coding:utf-8 -*-'''@Author : shouke'''import os.pathimport importlibdef load_plugin_modules():    '''递归加载当前目录下的所有模块'''    head, tail = os.path.split(__file__)    package_father_path, package = os.path.split(head)    def load_modules(dir_path):        nonlocal package_father_path        if not os.path.isdir(dir_path):            return        for name in os.listdir(dir_path):            full_path = os.path.join(dir_path, name)            if os.path.isdir(full_path):                load_modules(full_path)            elif not name.startswith('_') and name.endswith('.py'):                temp_path = full_path.replace(package_father_path, '')                relative_path = temp_path.replace('\\', '/').lstrip('/').replace('/', '.')                importlib.import_module(relative_path.rstrip('.py'), package=package)    load_modules(head)# 加载模块,自动触发装饰器,获取相关插件函数相关信息load_plugin_modules()

assertioner.py

#!/usr/bin/env python# -*- coding:utf-8 -*-'''@Author : shouke'''from components.decoraters.action_decorater import ActionDecoratorclass Assertioner(object):    @classmethod    @ActionDecorator.action_register('assert_equal', 'Assertioner', 'assert_equal', __name__)    def assert_equal(self,  config:dict, *args, **kwargs):        print('执行断言')        print('断言配置:\n', config)

send_request.py

#!/usr/bin/env python# -*- coding:utf-8 -*-'''@Author : shouke'''from components.decoraters.action_decorater import ActionDecoratorclass Requester(object):    @ActionDecorator.action_register('send_request', 'Requester', 'send_request', __name__)    @classmethod    def send_request(self, config:dict, *args, **kwargs):        print('发送请求')        print('请求配置:')        print(config)

example.py

#!/usr/bin/env python# -*- coding:utf-8 -*-'''@CreateTime: 2020/12/10 15:51@Author : shouke'''from components.decoraters.action_decorater import ActionDecoratorclass CustomClassName(object):    @ActionDecorator.action_register('custom_action_name', 'CustomClassName', 'action_func_name', __name__)    @classmethod    def action_func_name(self, config:dict, *args, **kwargs):        '''        example        user_instance: kwargs['user'] # 压测用户实例        '''        # do something you want# 说明 plugings目录下可自由创建python包,管理插件,当然,也可以位于components包下其它任意位置创建python包,管理插件(不推荐)

user.py

#!/usr/bin/env python# -*- coding:utf-8 -*-'''@Author : shouke'''from components.decoraters.action_decorater import ActionDecoratorclass User(object):    def __init__(self):        for action, action_map in ActionDecorator.ACTION_FUNC_CLASS_MODULE_MAP.items():            module = __import__(action_map.get('module_path'), fromlist=['True'])            class_cls = getattr(module, action_map.get('class_name'))            setattr(self, action, getattr(class_cls, action_map.get('function_name')))    def run_actions(self, actions):        ''' 执行一系列动作 '''        for step in actions:            action = step.get('action')            if hasattr(self, action):                getattr(self, action)(step, user=self)if __name__ == '__main__':    actions = [{         "action": "send_request",         "name": "请求登录", #可选配置,默认为None         "method": "POST",         "path": "/api/v1/login",         "body": {            "account": "shouke",            "password": "123456"         },         "headers": {             "Content-Type": "application/json"         }    },    {         "action": "assert_equal",         "name": "请求响应断言",         "target": "body",         "rule": "assert_contain",         "patterns": ["shouke","token"],         "logic":"or"    }]    User().run_actions(actions)

运行结果

发送请求

请求配置:

{'action': 'send_request', 'name': '请求登录', 'method': 'POST', 'path': '/api/v1/login', 'body': {'account': 'shouke', 'password': '123456'}, 'headers': {'Content-Type': 'application/json'}}

执行断言

断言配置:

{'action': 'assert_equal', 'name': '请求响应断言', 'target': 'body', 'rule': 'assert_contain', 'patterns': ['shouke', 'token'], 'logic': 'or'}

(0)

相关推荐

  • 非常干货:Python 探针实现原理

    作者丨mozillazg https://segmentfault.com/a/1190000004889212 本文呢,将简单讲述一下 Python 探针的实现原理.同时为了验证这个原理,我们也会一 ...

  • Traceback具体使用方法

    Traceback具体使用方法详解,首先在之前做Java的时候,异常对象默认就包含stacktrace相关的信息,通过异常对象的相关方法printStackTrace()和getStackTrace( ...

  • python之路—模块和包

    阅读目录 一 模块 3.1 import 3.2 from ... import... 3.3 把模块当做脚本执行 3.4 模块搜索路径 3.5 编译python文件 二 包 2.2 import 2 ...

  • 三分钟从入门到精通Python模块(我们在名为utils的模块中定义了一个名为乘法的函数)

    (我们在名为utils的模块中定义了一个名为乘法的函数) https://m.toutiao.com/is/JE2UqX5/ 什么是python中的模块: 假设您正在使用python解释器.您花了30 ...

  • 新手常见的python报错及解决方案

    此篇文章整理新手编写代码常见的一些错误,有些错误是粗心的错误,但对于新手而已,会折腾很长时间才搞定,所以在此总结下我遇到的一些问题.希望帮助到刚入门的朋友们.后续会不断补充. 目录 1.NameErr ...

  • Python程序设计入门与实践教程

    全书共9章.第1章讲解Python开发环境的搭建与使用,标准库与扩展库对象的导入与使用,以及Python代码编写规范.第2章在重点讲解内置函数和运算符的使用.第3章讲解选择结构.循环结构.异常处理结构 ...

  • 微信音乐小程序开发实践

    使用微信小程序实现个人音乐播放平台 一,效果展示 二,实现的功能 1,音乐播放,暂停,切换,歌词同步展示. 2,音乐收藏,根据搜索和播放历史推荐,播放排行榜展示. 三,数据库设计 1,基于功能需求,设 ...

  • 技术 | ​石墨烯锦纶6/粘胶混纺纱的开发实践

    摘要:介绍了石墨烯锦纶纤维原料的养生处理方法,石墨烯锦纶与粘胶的混和方式,以及石墨烯锦纶/粘胶混纺纱各工序工艺参数的优化.主要纺纱器材的选型.胶辊胶圈的处理方法. 关键词:石墨烯锦纶纤维:牵伸:养生: ...

  • 上汽乘用车:车身域控制系统开发实践

    声明:本文内容及图片由BC-AUTO转载至网络,信息来源于公众号汽车电子电气架构创新发展论坛.

  • 北大附小:项目引导式研学旅行实践探索(附案例分析)

    " 北大附小:项目引导式研学旅行实践探索 研学旅行在北大附小做了很长时间,只不过以前不叫研学旅行,叫"游学",国内的.国际的都有.文件颁布后,学校更加重视,要正式纳入国家 ...

  • 傻瓜式Android APP开发入门教程 | 菜鸟教程

    这篇文章主要介绍了Android APP开发入门教程,从SDK下载.开发环境搭建.代码编写.APP打包等步骤一一讲解,非常简明的一个Android APP开发入门教程,需要的朋友可以参考下. 工作中有 ...

  • 我是这样把“古典修身”变成招式、技巧的实践的

    正文: "性要纯,瞬间凝,心有间隙时,感思要流行".做到这一点,有人是天生的.有人却,需要从气息微妙控制开始练习. 依兰给我提了一点,她说古典句子和概念,建立不起来感觉.的确,我们 ...

  • 小程序开发实践:视图容器 view介绍,使用 view 搞定所有常见的 UI 布局

    目录 1,主要属性 1.1,hover-class 1.2,hover-stop-propagation 1.3,hover-start-time.hover-stay-time 1.4,拒绝300毫 ...

  • 技术 | 普梳100S高支纱的开发实践

    随着人们消费水平的提升和市场竞争的加剧,用户对纺织品的要求越来越高.纺织品向着高支高密的趋势发展,为了适应市场的需求,近几年我公司的棉纱产品结构向高支高密转变,大面积生产CM80到CM160高档精梳纱 ...