Python插件式程序设计与开发实践总结[Python基础]
插件式程序设计与开发实践总结
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
#!/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.path
import importlib
def 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 ActionDecorator
class Assertioner(object):
@classmethod
@ActionDecorator.action_register("assert_equal", "Assertioner", "assert_equal", __name__)
def assert_equal(self, config:dict, *args, **kwargs):
print("执行断言")
print("断言配置:
", config)
send_request.py
#!/usr/bin/env python# -*- coding:utf-8 -*-
"""
@Author : shouke
"""
from components.decoraters.action_decorater import ActionDecorator
class 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 ActionDecorator
class 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 ActionDecorator
class 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"}
以上是 Python插件式程序设计与开发实践总结[Python基础] 的全部内容, 来源链接: utcz.com/z/530581.html