Files
2026-07-13 12:34:57 +08:00

124 lines
4.6 KiB
Python

"""
组件模块发现与导入
负责注册阶段的延迟导入逻辑:
- import_modules: 批量导入模块(支持并行/串行),并报告慢模块
- build_discovered_components: 将组件元数据转换为包含实际类/函数对象的组件条目
这些函数为纯函数,由 AutoConfigurationManager 调用并传入所需状态,
不依赖 auto_configuration 模块,避免循环导入。
"""
import time
import importlib
from typing import Dict, List, Any, Set
from loguru import logger as loguru_logger
logger = loguru_logger.bind(name=__name__)
def _import_single(module_name: str):
"""导入单个模块并返回结果"""
try:
start = time.perf_counter()
module = importlib.import_module(module_name)
elapsed = (time.perf_counter() - start) * 1000
return module_name, module, elapsed, None
except Exception as e:
return module_name, None, 0, e
def import_modules(modules_to_import: Set[str], parallel_import: bool = False) -> Dict[str, Any]:
"""批量导入模块(支持并行/串行),返回 模块名 -> 模块 的映射
导入失败的模块会记录 error 日志并被跳过;导入耗时超过 100ms 的模块
会汇总到一条 warning 日志中。
"""
imported_modules: Dict[str, Any] = {}
slow_modules = [] # 记录慢模块
if parallel_import and len(modules_to_import) > 1:
# 并行导入(对 I/O 密集的模块有帮助)
from concurrent.futures import ThreadPoolExecutor, as_completed
with ThreadPoolExecutor(max_workers=min(8, len(modules_to_import))) as executor:
futures = {executor.submit(_import_single, m): m for m in modules_to_import}
for future in as_completed(futures):
module_name, module, elapsed, error = future.result()
if error:
logger.error(f"导入模块失败 {module_name}: {error}")
else:
imported_modules[module_name] = module
if elapsed > 100:
slow_modules.append((module_name, elapsed))
else:
# 串行导入
for module_name in modules_to_import:
module_name, module, elapsed, error = _import_single(module_name)
if error:
logger.error(f"导入模块失败 {module_name}: {error}")
else:
imported_modules[module_name] = module
if elapsed > 100:
slow_modules.append((module_name, elapsed))
# 输出慢模块报告
if slow_modules:
slow_modules.sort(key=lambda x: x[1], reverse=True)
report = ", ".join([f"{name}({ms:.0f}ms)" for name, ms in slow_modules[:10]])
logger.warning(f"慢模块导入: {report}")
return imported_modules
def build_discovered_components(
component_metadata: Dict[str, List[dict]],
parallel_import: bool = False,
) -> Dict[str, List[dict]]:
"""将组件元数据转换为包含实际类对象的组件条目
收集需导入的模块并去重,执行导入后将元数据解析为实际的类/函数/方法对象。
返回与 component_metadata 同结构的 discovered_components 字典。
"""
# 收集需要导入的模块(去重)
modules_to_import: Set[str] = set()
for items in component_metadata.values():
for item in items:
modules_to_import.add(item['module'])
# 批量导入模块
imported_modules = import_modules(modules_to_import, parallel_import)
# 将元数据转换为包含实际类对象的组件
discovered_components: Dict[str, List[dict]] = {key: [] for key in component_metadata.keys()}
for component_type, items in component_metadata.items():
for item in items:
module = imported_modules.get(item['module'])
if not module:
continue
entry = {'module': item['module'], 'type': item['type']}
if 'class_name' in item:
cls = getattr(module, item['class_name'], None)
if cls:
entry['class'] = cls
else:
continue
if 'func_name' in item:
func = getattr(module, item['func_name'], None)
if func:
entry['function'] = func
else:
continue
if 'method_name' in item:
entry['method_name'] = item['method_name']
if 'class' in entry:
entry['method'] = getattr(entry['class'], item['method_name'], None)
discovered_components[component_type].append(entry)
return discovered_components