- crypto.py: API Key 加密/解密工具 - runtime_config.py: 运行时动态配置管理 - log_buffer.py: 内存日志缓冲区 - config.py: 新增加密配置项 - http_client.py: 增强重试和错误处理
69 lines
1.8 KiB
Python
69 lines
1.8 KiB
Python
"""数据源协议 + 注册表。
|
|
|
|
定义 DataSource 契约,并提供全局注册表供路由层分发。
|
|
每个比赛数据源实现该协议,注册后即可通过统一入口调度。
|
|
|
|
注: injuries 是球员级独立领域(写 Injury 表),不遵循此协议。
|
|
"""
|
|
from __future__ import annotations
|
|
|
|
from typing import Protocol
|
|
|
|
from src.db.base import AsyncSession
|
|
|
|
|
|
class DataSource(Protocol):
|
|
"""比赛数据源契约:抓取 → 规范化 → 入库。"""
|
|
|
|
@property
|
|
def name(self) -> str:
|
|
"""数据源标识名(用于路由/日志)。"""
|
|
...
|
|
|
|
async def ingest(self, db: AsyncSession, **kwargs) -> dict:
|
|
"""执行完整采集流程,返回统计。"""
|
|
...
|
|
|
|
|
|
# ── 注册表 ──
|
|
_SOURCES: dict[str, DataSource] = {}
|
|
|
|
|
|
def register(source):
|
|
"""装饰器:将数据源注册到全局注册表。
|
|
|
|
兼容类注册与实例注册:类会被实例化后存入(保证 get_source 返回实例)。
|
|
"""
|
|
obj = source() if isinstance(source, type) else source
|
|
_SOURCES[obj.name] = obj
|
|
return source
|
|
|
|
|
|
def get_source(name: str) -> DataSource:
|
|
"""按名获取数据源。"""
|
|
if not _SOURCES:
|
|
_load_sources()
|
|
if name not in _SOURCES:
|
|
raise ValueError(f"未知数据源: {name}")
|
|
return _SOURCES[name]
|
|
|
|
|
|
def list_sources() -> list[str]:
|
|
"""列出所有已注册数据源名。"""
|
|
if not _SOURCES:
|
|
_load_sources()
|
|
return list(_SOURCES.keys())
|
|
|
|
|
|
def _load_sources() -> None:
|
|
"""延迟导入数据源触发 @register(避免循环导入)。"""
|
|
from src.data.bzzoiro import BzzoiroSource # noqa: F811
|
|
from src.data.understat import UnderstatSource # noqa: F811
|
|
|
|
|
|
# 保持向后兼容:模块加载时尝试加载(但不再强制)
|
|
try:
|
|
_load_sources()
|
|
except Exception:
|
|
pass
|