mirror of
https://github.com/fastapi-practices/fastapi-best-architecture.git
synced 2026-09-22 05:22:56 +00:00
149 lines
4.4 KiB
Python
149 lines
4.4 KiB
Python
import inspect
|
|
|
|
from typing import Any
|
|
|
|
from fastapi import FastAPI
|
|
|
|
from backend.common.enums import LifespanStage
|
|
from backend.common.lifespan import lifespan_manager
|
|
from backend.common.log import log
|
|
from backend.plugin.core import get_ordered_enabled_plugins
|
|
from backend.plugin.errors import PluginInjectError
|
|
from backend.utils.async_helper import run_await
|
|
from backend.utils.dynamic_import import import_module_cached
|
|
|
|
|
|
def register_plugin_lifespan_hook(plugin: str, module: Any) -> None:
|
|
"""
|
|
注册插件 lifespan hook
|
|
|
|
:param plugin: 插件名称
|
|
:param module: 插件 hooks 模块
|
|
:return:
|
|
"""
|
|
lifespan_hook = getattr(module, 'lifespan', None)
|
|
if lifespan_hook is None:
|
|
return
|
|
|
|
if not callable(lifespan_hook):
|
|
log.warning(f'插件 {plugin} 的 lifespan 不是可调用对象,已跳过')
|
|
return
|
|
|
|
lifespan_manager.register(lifespan_hook, stage=LifespanStage.plugin) # type: ignore[call-overload]
|
|
log.info(f'插件 {plugin} lifespan hook 注册成功')
|
|
|
|
|
|
def run_plugin_setup_hook(plugin: str, module: Any, app: FastAPI) -> None:
|
|
"""
|
|
执行插件 setup hook
|
|
|
|
:param plugin: 插件名称
|
|
:param module: 插件 hooks 模块
|
|
:param app: FastAPI 应用实例
|
|
:return:
|
|
"""
|
|
setup_hook = getattr(module, 'setup', None)
|
|
if setup_hook is None:
|
|
return
|
|
|
|
if not callable(setup_hook):
|
|
log.warning(f'插件 {plugin} 的 setup 不是可调用对象,已跳过')
|
|
return
|
|
|
|
setup_result = setup_hook(app)
|
|
if inspect.isawaitable(setup_result):
|
|
run_await(lambda: setup_result)() # type: ignore
|
|
log.info(f'插件 {plugin} setup hook 执行成功')
|
|
|
|
|
|
def run_plugin_otel_hook(plugin: str, module: Any, app: FastAPI) -> None:
|
|
"""
|
|
执行插件 OpenTelemetry hook
|
|
|
|
:param plugin: 插件名称
|
|
:param module: 插件 hooks 模块
|
|
:param app: FastAPI 应用实例
|
|
:return:
|
|
"""
|
|
otel_hook = getattr(module, 'otel', None)
|
|
if otel_hook is None:
|
|
return
|
|
|
|
if not callable(otel_hook):
|
|
log.warning(f'插件 {plugin} 的 otel 不是可调用对象,已跳过')
|
|
return
|
|
|
|
otel_result = otel_hook(app)
|
|
if inspect.isawaitable(otel_result):
|
|
run_await(lambda: otel_result)() # type: ignore
|
|
log.info(f'插件 {plugin} otel hook 执行成功')
|
|
|
|
|
|
def _get_plugin_hook_modules() -> list[tuple[str, Any]]:
|
|
"""
|
|
获取插件 hooks 模块
|
|
|
|
:return:
|
|
"""
|
|
plugin_hook_modules: list[tuple[str, Any]] = []
|
|
|
|
for plugin in get_ordered_enabled_plugins():
|
|
module_path = f'backend.plugin.{plugin.name}.hooks'
|
|
try:
|
|
module = import_module_cached(module_path)
|
|
except ModuleNotFoundError as e:
|
|
if e.name == module_path:
|
|
continue
|
|
log.warning(f'插件 {plugin.name} hooks 加载失败: {e}')
|
|
continue
|
|
except Exception as e:
|
|
log.warning(f'插件 {plugin.name} hooks 加载失败: {e}')
|
|
continue
|
|
|
|
plugin_hook_modules.append((plugin.name, module))
|
|
|
|
return plugin_hook_modules
|
|
|
|
|
|
def register_plugin_hooks(app: FastAPI) -> None:
|
|
"""
|
|
注册并执行插件 hooks
|
|
|
|
:param app: FastAPI 应用实例
|
|
:return:
|
|
"""
|
|
|
|
def run_setup_hook(plugin: str, module: Any) -> None:
|
|
try:
|
|
register_plugin_lifespan_hook(plugin, module)
|
|
except Exception as e:
|
|
log.exception(f'插件 {plugin} lifespan hooks 执行失败: {e}')
|
|
raise PluginInjectError(f'插件 {plugin} lifespan hooks 执行失败:{e!s}') from e
|
|
try:
|
|
run_plugin_setup_hook(plugin, module, app)
|
|
except Exception as e:
|
|
log.exception(f'插件 {plugin} setup hooks 执行失败: {e}')
|
|
raise PluginInjectError(f'插件 {plugin} setup hooks 执行失败:{e!s}') from e
|
|
|
|
for plugin, module in _get_plugin_hook_modules():
|
|
run_setup_hook(plugin, module)
|
|
|
|
|
|
def init_plugin_otel_hooks(app: FastAPI) -> None:
|
|
"""
|
|
初始化插件 OpenTelemetry hooks
|
|
|
|
:param app: FastAPI 应用实例
|
|
:return:
|
|
"""
|
|
|
|
def run_otel_hook(plugin: str, module: Any) -> None:
|
|
try:
|
|
run_plugin_otel_hook(plugin, module, app)
|
|
except Exception as e:
|
|
log.exception(f'插件 {plugin} otel hook 执行失败: {e}')
|
|
raise PluginInjectError(f'插件 {plugin} otel hook 执行失败:{e!s}') from e
|
|
|
|
for plugin, module in _get_plugin_hook_modules():
|
|
run_otel_hook(plugin, module)
|