You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
DjangoBlog/djangoblog/plugin_manage/loader.py

124 lines
4.9 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

# 插件加载与管理模块
# 该模块提供了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]