# core/plugin.py from abc import ABC, abstractmethod import importlib from enum import Enum import os,sys import pkgutil import inspect from typing import Dict, List, Type sys.path.append(os.path.join(os.path.dirname(__file__), '..')) from core.behavior_tree import ReleaseFlowAction from core.defines import ReleasePhase, ProcessStep, Dict import core.logger as logger log = logger.get_logger() """ # format: PluginName.ReleaseStep.ProcessStep ReleaseFlowCommon.StepEnv.Pre """ class PluginType(str, Enum): """ 为插件分类,目前仅有一种插件,FLOW类型的插件用于执行Release Flow """ FLOW = 'flow' class PluginInterface(ABC): """PluginInterface 插件基类""" def __init__(self, type: PluginType): self.type = type def get_type(self) -> PluginType: return self.type @abstractmethod def get_version(self) -> str: pass @abstractmethod def get_description(self) -> str: """返回插件描述""" pass class ReleaseFlowPlugin(PluginInterface, ABC): """ReleaseFlowPlugin 插件基类""" def __init__(self): PluginInterface.__init__(self, PluginType.FLOW) self.btrees:Dict = Dict() @abstractmethod def get_version(self) -> str: """返回插件版本""" pass @abstractmethod def get_description(self) -> str: """返回插件描述""" pass # 支持使用.操作符访问btrees def __getattr__(self, name: str) -> Dict: return self.btrees[name] def ReleaseFlowPluginDecorator(cls): """装饰器,用于自动注册ReleaseFlow插件, 每个 ReleaseFlowPlugin 插件上加上这个装饰器, 插件初始化时会自动搜索并注册行为树 Args: cls: 插件类 Returns: 插件类 """ original_init = cls.__init__ # 如果不是ReleaseFlowPlugin的子类,则不进行装饰 if not issubclass(cls, ReleaseFlowPlugin): return cls def behavior_tree_register(self: ReleaseFlowPlugin): """ 搜索当前目录下的所有行为树节点,并根据命名注册到btrees中 """ def _load_actions_from_package(self, package_name: str): """从包中加载插件""" package = importlib.import_module(package_name) for _, name, is_pkg in pkgutil.iter_modules(package.__path__, f"{package_name}."): if not is_pkg: _load_actions(self, name) else: _load_actions_from_package(self, f"{package_name}.{name}") def _load_actions(self, module_name: str): """加载单个behavior action""" # log.debug(f"Try to check action: {module_name}") module = importlib.import_module(module_name) # 遍历模块中的所有属性名称 for attribute_name in dir(module): attribute = getattr(module, attribute_name) if not isinstance(attribute, type): # 检查是否是类 continue if not issubclass(attribute, ReleaseFlowAction): # 检查是否是插件类 continue if inspect.isabstract(attribute): # 检查是否是抽象类 continue log.info(f"Loaded action: {attribute.__name__}") self.sub_actions[attribute.__name__] = attribute def discover_actions(self:ReleaseFlowPlugin): module = inspect.getmodule(self) path = os.path.relpath(os.path.dirname(module.__file__)).replace('/', '.') # log.debug(f"Try to search action in: {path}") self.sub_actions = {} for _, name, is_pkg in pkgutil.iter_modules([os.path.dirname(module.__file__)]): # log.trace(f"Try to load action: {name}") if is_pkg: _load_actions_from_package(self, f"{path}.{name}") else: _load_actions(self, f"{path}.{name}") if not isinstance(self, ReleaseFlowPlugin): log.warning(f"Plugin class {self.__class__.__name__} is not a ReleaseFlowPlugin") raise ReleaseErr("The plugin class decorator is not a ReleaseFlowPlugin") discover_actions(self) for phase in ReleasePhase: phase = phase.value self.btrees[phase] = Dict() for step in ProcessStep: step = step.value action_class = f"_{phase}_{step}" if not self.sub_actions.get(action_class): log.trace(f"Action {action_class} not found") continue log.debug(f"Register ReleaseFlow behavior tree node: {action_class}") if not self.btrees[phase].get(step): self.btrees[phase][step] = self.sub_actions[action_class] else: log.warning(f"Behavior tree node {action_class} already exists: {self.btrees[phase][step]}") def new_init(self, *args, **kwargs): original_init(self, *args, **kwargs) behavior_tree_register(self) # 添加标记,表示该类使用了flow action 装饰器,用于检查是否注册了行为树 cls._is_action_registered = True cls.__init__ = new_init return cls class PluginManager: """插件管理器""" def __init__(self, plugin_dir: str = "plugins"): self.plugin_dir = plugin_dir self.plugins: Dict[str, Type[PluginInterface]] = {} self.loaded_plugins: Dict[str, PluginInterface] = {} self.discover_plugins() def discover_plugins(self): """发现并加载所有插件""" for _, name, is_pkg in pkgutil.iter_modules([self.plugin_dir]): plugin_dir = f"{self.plugin_dir}.{name}" log.trace(f"Try to search plugin in: {plugin_dir}") if is_pkg: self._load_plugins_from_package(plugin_dir) else: self._load_plugin(plugin_dir) def _load_plugins_from_package(self, package_name: str): """从包中加载插件""" package = importlib.import_module(package_name) for _, name, is_pkg in pkgutil.iter_modules(package.__path__, f"{package_name}."): if not is_pkg: self._load_plugin(name) else: self._load_plugins_from_package(f"{package_name}.{name}") def _load_plugin(self, module_name: str): """加载单个插件""" module = importlib.import_module(module_name) for attribute_name in dir(module): plugin_class = getattr(module, attribute_name) if not isinstance(plugin_class, type): # 检查是否是类 continue if not issubclass(plugin_class, PluginInterface): # 检查是否是插件类 continue if inspect.isabstract(plugin_class): # 检查是否是抽象类 continue # 检查插件类是否使用了 ReleaseFlowActionRegister 装饰器 if issubclass(plugin_class, ReleaseFlowPlugin) and not hasattr(plugin_class, '_is_action_registered'): log.warning(f"Plugin class {plugin_class.__name__} does not have the ReleaseFlowActionRegister decorator.") continue log.info(f"Loaded plugin: {plugin_class.__name__}") self.plugins[plugin_class.__name__] = plugin_class def get_plugin(self, name: str) -> PluginInterface: """获取插件实例, 每个插件实例只初始化一次 Args: name: 插件名称 Returns: 插件实例 """ if name not in self.loaded_plugins: if name in self.plugins: self.loaded_plugins[name] = self.plugins[name]() else: raise ValueError(f"Plugin {name} not found") return self.loaded_plugins[name] def get_available_plugins(self) -> List[str]: """获取所有可用插件名称""" return list(self.plugins.keys()) def __str__(self) -> str: string = "" string += f"Plugin directory: {self.plugin_dir}\n" string += "Available plugins:" string += "[" + " \n".join(self.plugins.keys()) + "]\n" string += "Loaded plugins:" string += "[" + " \n".join(self.loaded_plugins.keys()) + "]\n" return string def __repr__(self) -> str: return str(self) def __getattr__(self, name: str) -> PluginInterface: # 获取插件实例 plugin = self.get_plugin(name) return plugin