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

By:授客 QQ:1033553122

开发环境

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

  1. #!/usr/bin/env python
  2. # -*- coding:utf-8 -*-
  3.  
  4. '''
  5. @CreateTime: 2020/12/09 14:58
  6. @Author : shouke
  7. '''
  8.  
  9. class ActionDecorator(object):
  10. '''
  11. action 装饰器
  12. '''
  13.  
  14. ACTION_FUNC_CLASS_MODULE_MAP = {}
  15.  
  16. @classmethod
  17. def action_register(cls, action, class_name, function_name, module_path):
  18. def wrapper(func):
  19. cls.ACTION_FUNC_CLASS_MODULE_MAP.update({action: {'class_name':class_name, 'function_name':function_name, 'module_path':module_path}})
  20. return func
  21. return wrapper

  

components/__init__.py

  1. #!/usr/bin/env python
  2. # -*- coding:utf-8 -*-
  3.  
  4. '''
  5. @Author : shouke
  6. '''
  7.  
  8. import os.path
  9. import importlib
  10.  
  11. def load_plugin_modules():
  12. '''递归加载当前目录下的所有模块'''
  13.  
  14. head, tail = os.path.split(__file__)
  15. package_father_path, package = os.path.split(head)
  16.  
  17. def load_modules(dir_path):
  18. nonlocal package_father_path
  19. if not os.path.isdir(dir_path):
  20. return
  21. for name in os.listdir(dir_path):
  22. full_path = os.path.join(dir_path, name)
  23. if os.path.isdir(full_path):
  24. load_modules(full_path)
  25. elif not name.startswith('_') and name.endswith('.py'):
  26. temp_path = full_path.replace(package_father_path, '')
  27. relative_path = temp_path.replace('\\', '/').lstrip('/').replace('/', '.')
  28. importlib.import_module(relative_path.rstrip('.py'), package=package)
  29. load_modules(head)
  30.  
  31. # 加载模块,自动触发装饰器,获取相关插件函数相关信息
  32.  
  33. load_plugin_modules()

  

assertioner.py

  1. #!/usr/bin/env python
  2. # -*- coding:utf-8 -*-
  3.  
  4. '''
  5. @Author : shouke
  6. '''
  7.  
  8. from components.decoraters.action_decorater import ActionDecorator
  9.  
  10. class Assertioner(object):
  11. @classmethod
  12. @ActionDecorator.action_register('assert_equal', 'Assertioner', 'assert_equal', __name__)
  13. def assert_equal(self, config:dict, *args, **kwargs):
  14. print('执行断言')
  15. print('断言配置:\n', config)

  

send_request.py

  1. #!/usr/bin/env python
  2. # -*- coding:utf-8 -*-
  3.  
  4. '''
  5. @Author : shouke
  6. '''
  7.  
  8. from components.decoraters.action_decorater import ActionDecorator
  9.  
  10. class Requester(object):
  11. @ActionDecorator.action_register('send_request', 'Requester', 'send_request', __name__)
  12. @classmethod
  13. def send_request(self, config:dict, *args, **kwargs):
  14. print('发送请求')
  15. print('请求配置:')
  16. print(config)

  

example.py

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

  

user.py

  1. #!/usr/bin/env python
  2. # -*- coding:utf-8 -*-
  3.  
  4. '''
  5. @Author : shouke
  6. '''
  7.  
  8. from components.decoraters.action_decorater import ActionDecorator
  9.  
  10. class User(object):
  11.  
  12. def __init__(self):
  13. for action, action_map in ActionDecorator.ACTION_FUNC_CLASS_MODULE_MAP.items():
  14. module = __import__(action_map.get('module_path'), fromlist=['True'])
  15. class_cls = getattr(module, action_map.get('class_name'))
  16. setattr(self, action, getattr(class_cls, action_map.get('function_name')))
  17.  
  18. def run_actions(self, actions):
  19. ''' 执行一系列动作 '''
  20.  
  21. for step in actions:
  22. action = step.get('action')
  23.  
  24. if hasattr(self, action):
  25. getattr(self, action)(step, user=self)
  26.  
  27. if __name__ == '__main__':
  28. actions = [{
  29. "action": "send_request",
  30. "name": "请求登录", #可选配置,默认为None
  31. "method": "POST",
  32. "path": "/api/v1/login",
  33. "body": {
  34. "account": "shouke",
  35. "password": "123456"
  36. },
  37. "headers": {
  38. "Content-Type": "application/json"
  39. }
  40. },
  41. {
  42. "action": "assert_equal",
  43. "name": "请求响应断言",
  44. "target": "body",
  45. "rule": "assert_contain",
  46. "patterns": ["shouke","token"],
  47. "logic":"or"
  48. }]
  49. 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'}

Python 插件式程序设计与开发实践总结的更多相关文章

  1. 响应式WEB页面开发实践

    转自:https://github.com/markyun/My-blog/issues/27 最近得到一个新任务单,让我用一套页面适应所有主流终端(Android.iPhone.iPad.PC),而 ...

  2. Chrome插件(Extensions)开发实践

    内容摘自:http://www.cnblogs.com/mfryf/p/3701801.html

  3. WebApi 插件式构建方案:发现并加载程序集

    插件式的 WebApi 开发,首要面对的问题就是程序集的发现.因为开发的过程中,都是在各自的解决方案下进行开发,部署后是分模块放在一个整体的的运行时网站下. 约定 这里我根据上一节的设定,把插件打包完 ...

  4. 《Flask Web开发——基于Python的Web应用开发实践》一字一句上机实践(上)

    目录 前言 第1章 安装 第2章 程序的基本结构 第3章 模板 第4章 Web表单 第5章 数据库 第6章 电子邮件 第7章 大型程序的结构   前言 学习Python也有一个半月时间了,学到现在感觉 ...

  5. 基于Flask的Web应用程序插件式结构开发

    事实上,很多应用程序基于插件式结构开发,可以很方便了扩展软件的功能,并且这些功能完全可以依托于第三方开发者,只要提供好接口和完备文档,比如wordpress.谷歌火狐浏览器等. Python这样的动态 ...

  6. 也来学学插件式开发续-利用MEF

    前面一个博客:也来学学插件式开发中很多朋友留言说可以用MEF来实现.于是我就试着用MEF实现了一下. 步骤和上一篇差不多,只是加载插件的方式有所不同.这只是一个自己的示例程序,肯定有很多不足之处,欢迎 ...

  7. MEF 插件式开发 - 小试牛刀

    原文:MEF 插件式开发 - 小试牛刀 目录 MEF 简介 实践出真知 面向接口编程 控制反转(IOC) 构建入门级 MEF 相关参考 MEF 简介 Managed Extensibility Fra ...

  8. 从零开始实现ASP.NET Core MVC的插件式开发(七) - 近期问题汇总及部分解决方案

    标题:从零开始实现ASP.NET Core MVC的插件式开发(七) - 问题汇总及部分解决方案 作者:Lamond Lu 地址:https://www.cnblogs.com/lwqlun/p/12 ...

  9. AppDomain实现【插件式】开发

    前言: 近期项目中需要实现"热插拔"式的插件程序,例如:定义一个插件接口:由不同开发人员实现具体的插件功能类库:并最终在应用中调用具体插件功能. 此时需要考虑:插件执行的安全性(隔 ...

  10. Redis的Python实践,以及四中常用应用场景详解——学习董伟明老师的《Python Web开发实践》

    首先,简单介绍:Redis是一个基于内存的键值对存储系统,常用作数据库.缓存和消息代理. 支持:字符串,字典,列表,集合,有序集合,位图(bitmaps),地理位置,HyperLogLog等多种数据结 ...

随机推荐

  1. 如何使用Charles查看页面接口请求?

    1.Charles下载地址: https://www.charlesproxy.com/latest-release/download.do 2.如何抓取浏览器的操作 2.1 点击映射 2.2 点击映 ...

  2. 什么是Base64算法

    HTTP是超文本传输协议,所以HTTP协议中请求.相应都是以ASCII字符方式传输,如果要传输二进制需要经过BASE64或MIME等编码(因为HTTP协议pop3.smtp邮件协议都是针对文本的,而F ...

  3. Qt-FFmpeg开发-音频解码为PCM文件(9)

    音视频/FFmpeg #Qt Qt-FFmpeg开发-使用libavcodec API的音频解码示例(MP3转pcm) 目录 音视频/FFmpeg #Qt Qt-FFmpeg开发-使用libavcod ...

  4. Mysql 创建索引语句

    mysql有哪些索引 index 普通索引 alter table table_name add index index_name(column) 最基本的索引,没有任何限制 primary key ...

  5. Vue学习:15.组件化开发

    组件化开发 组件化开发是一种软件开发方法,它将应用程序拆分成独立的.可重用的模块,每个模块都被称为组件.这些组件可以独立开发.测试.维护和部署,从而提高了代码的可维护性.可扩展性和复用性.在前端开发中 ...

  6. 怎么实现鼠标移入第i个li则对应显示第i个div,默认显示第一个LI

    html 部分 <ul> <li>菜单1</li> <li>菜单2</li> <li>菜单3</li> <li ...

  7. CM 停用 Parcel 异常

    在将Doris集成到CM时,第一次打的包存在问题,想更新下,停用.删除Parcel时出现了问题卡住了,一直显示75%.无奈换了名称和版本,分配.激活,然后又卡在了75%,点开后,发现是同一台机器.其a ...

  8. 接口加密传输设计及AES加解密代码DEMO

    接口加密传输设计及AES加解密代码DEMO 接口加密的方案设计:可以将请求的json字符串aes加密,通过params字段传输,接口服务端接收到参数,先解密,然后转换成对象.继续业务逻辑的处理.(另外 ...

  9. jwt 加密和解密demo

    jwt 加密和解密demo JSON Web Token(JWT)是一个非常轻巧的规范.这个规范允许我们使用 JWT 在用户和服务器之间传递安全可靠的信息.导入jar <dependency&g ...

  10. CountDownLatch demo演示裁判和选手赛跑

    # CountDownLatch demo演示裁判和选手赛跑 package com.example.core.mydemo; import java.util.concurrent.CountDow ...