|
|
# 插件加载与管理模块
|
|
|
# 该模块提供了Django应用中插件的动态加载、注册和查询功能
|
|
|
# 主要功能包括:从指定目录加载激活的插件、维护插件注册表、提供多种插件查询接口
|
|
|
# 插件需符合特定结构(包含plugin.py及插件实例),通过Django配置指定激活的插件和插件目录
|
|
|
|
|
|
import os
|
|
|
import logging
|
|
|
from django.conf import settings
|
|
|
|
|
|
# 初始化日志记录器,用于记录插件加载过程中的信息和错误
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
# 全局插件注册表,存储所有已成功加载的插件实例
|
|
|
_loaded_plugins = []
|
|
|
|
|
|
|
|
|
def load_plugins():
|
|
|
"""
|
|
|
从'plugins'目录动态加载并初始化激活的插件。
|
|
|
该函数应在Django应用注册表就绪后调用(确保Django配置已加载)。
|
|
|
加载逻辑:遍历配置中激活的插件列表,检查插件目录结构完整性,导入并初始化插件实例。
|
|
|
"""
|
|
|
global _loaded_plugins
|
|
|
# 重置插件注册表,避免重复加载
|
|
|
_loaded_plugins = []
|
|
|
|
|
|
# 遍历配置中激活的插件名称列表(settings.ACTIVE_PLUGINS定义需加载的插件)
|
|
|
for plugin_name in settings.ACTIVE_PLUGINS:
|
|
|
# 构建插件目录的绝对路径(settings.PLUGINS_DIR为插件根目录)
|
|
|
plugin_path = os.path.join(settings.PLUGINS_DIR, plugin_name)
|
|
|
# 检查插件目录是否存在且包含必要的plugin.py文件(插件入口)
|
|
|
if os.path.isdir(plugin_path) and os.path.exists(os.path.join(plugin_path, 'plugin.py')):
|
|
|
try:
|
|
|
# 动态导入插件模块:从plugins.插件名.plugin导入模块
|
|
|
# fromlist=['plugin']确保导入子模块而非父模块
|
|
|
plugin_module = __import__(f'plugins.{plugin_name}.plugin', fromlist=['plugin'])
|
|
|
|
|
|
# 检查导入的模块是否包含'plugin'属性(插件实例)
|
|
|
if hasattr(plugin_module, 'plugin'):
|
|
|
# 获取插件实例并添加到全局注册表
|
|
|
plugin_instance = plugin_module.plugin
|
|
|
_loaded_plugins.append(plugin_instance)
|
|
|
# 记录成功加载日志,包含插件名称和插件定义的名称
|
|
|
logger.info(f"Successfully loaded plugin: {plugin_name} - {plugin_instance.PLUGIN_NAME}")
|
|
|
else:
|
|
|
# 插件模块结构不完整(缺少plugin实例)时记录警告
|
|
|
logger.warning(f"Plugin {plugin_name} does not have 'plugin' instance")
|
|
|
|
|
|
except ImportError as e:
|
|
|
# 捕获导入错误(如模块不存在、依赖缺失等)
|
|
|
logger.error(f"Failed to import plugin: {plugin_name}", exc_info=e)
|
|
|
except AttributeError as e:
|
|
|
# 捕获属性错误(如插件实例缺少必要属性)
|
|
|
logger.error(f"Failed to get plugin instance: {plugin_name}", exc_info=e)
|
|
|
except Exception as e:
|
|
|
# 捕获其他未预期的错误
|
|
|
logger.error(f"Unexpected error loading plugin: {plugin_name}", exc_info=e)
|
|
|
|
|
|
|
|
|
def get_loaded_plugins():
|
|
|
"""
|
|
|
获取所有已加载的插件实例列表。
|
|
|
|
|
|
返回:
|
|
|
list: 包含所有成功加载的插件实例的列表
|
|
|
"""
|
|
|
return _loaded_plugins
|
|
|
|
|
|
|
|
|
def get_plugin_by_name(plugin_name):
|
|
|
"""
|
|
|
根据插件名称查询插件实例(实际查询的是plugin_slug属性,可能与函数名存在命名兼容)。
|
|
|
|
|
|
参数:
|
|
|
plugin_name: 要查询的插件slug名称
|
|
|
|
|
|
返回:
|
|
|
匹配的插件实例,若未找到则返回None
|
|
|
"""
|
|
|
for plugin in _loaded_plugins:
|
|
|
if plugin.plugin_slug == plugin_name:
|
|
|
return plugin
|
|
|
return None
|
|
|
|
|
|
|
|
|
def get_plugin_by_slug(plugin_slug):
|
|
|
"""
|
|
|
根据插件slug查询插件实例(与plugin_slug属性精确匹配)。
|
|
|
|
|
|
参数:
|
|
|
plugin_slug: 要查询的插件slug标识
|
|
|
|
|
|
返回:
|
|
|
匹配的插件实例,若未找到则返回None
|
|
|
"""
|
|
|
for plugin in _loaded_plugins:
|
|
|
if plugin.plugin_slug == plugin_slug:
|
|
|
return plugin
|
|
|
return None
|
|
|
|
|
|
|
|
|
def get_plugins_info():
|
|
|
"""
|
|
|
获取所有已加载插件的信息字典列表。
|
|
|
信息由插件的get_plugin_info()方法提供,通常包含名称、描述、版本等元数据。
|
|
|
|
|
|
返回:
|
|
|
list: 每个元素为一个插件的信息字典
|
|
|
"""
|
|
|
return [plugin.get_plugin_info() for plugin in _loaded_plugins]
|
|
|
|
|
|
|
|
|
def get_plugins_by_position(position):
|
|
|
"""
|
|
|
获取支持指定位置的所有插件实例(基于插件的SUPPORTED_POSITIONS属性筛选)。
|
|
|
用于在页面特定位置渲染插件内容(如侧边栏、页头等)。
|
|
|
|
|
|
参数:
|
|
|
position: 位置标识(如'sidebar'、'header'等,对应POSITION_HOOKS中的键)
|
|
|
|
|
|
返回:
|
|
|
list: 所有支持该位置的插件实例
|
|
|
"""
|
|
|
return [plugin for plugin in _loaded_plugins if position in plugin.SUPPORTED_POSITIONS] |