chore: 升级到 Python 3.14 并现代化代码

- 升级 Docker 基础镜像从 Python 3.12 到 3.14
- 更新 pyproject.toml 支持 Python 3.13/3.14
- 移除 Python 3.8/3.9/3.10/3.11 分类器
- 更新 black 和 mypy 配置目标版本
- 将 get_event_loop() 替换为 get_running_loop() 加上 RuntimeError 处理
- 简化 compute_cost_sync 中的 asyncio.run 使用
- Dict/List/Tuple/Set → dict/list/tuple/set (PEP 585)
- Optional[T] → T | None (PEP 604)
- Union[A, B] → A | B (PEP 604)
- 移除废弃的 typing 导入
- 移除不必要的字符串引号注解
This commit is contained in:
AAEE86
2026-01-30 03:10:21 +08:00
parent 3e75bc8964
commit 24d24f6829
255 changed files with 4062 additions and 4173 deletions

View File

@@ -8,11 +8,13 @@
3. 连接池复用Keep-alive 连接减少 TCP 握手开销
"""
from __future__ import annotations
import asyncio
import hashlib
import time
from contextlib import asynccontextmanager
from typing import Any, Dict, Optional, Tuple
from typing import Any
from urllib.parse import quote, urlparse
import httpx
@@ -26,7 +28,7 @@ _proxy_clients_lock = asyncio.Lock()
_default_client_lock = asyncio.Lock()
def _compute_proxy_cache_key(proxy_config: Optional[Dict[str, Any]]) -> str:
def _compute_proxy_cache_key(proxy_config: dict[str, Any] | None) -> str:
"""
计算代理配置的缓存键
@@ -48,7 +50,7 @@ def _compute_proxy_cache_key(proxy_config: Optional[Dict[str, Any]]) -> str:
return f"proxy:{hashlib.md5(proxy_url.encode()).hexdigest()[:16]}"
def build_proxy_url(proxy_config: Dict[str, Any]) -> Optional[str]:
def build_proxy_url(proxy_config: dict[str, Any]) -> str | None:
"""
根据代理配置构建完整的代理 URL
@@ -103,11 +105,11 @@ class HTTPClientPool:
3. LRU 淘汰:代理客户端超过上限时淘汰最久未使用的
"""
_instance: Optional["HTTPClientPool"] = None
_default_client: Optional[httpx.AsyncClient] = None
_clients: Dict[str, httpx.AsyncClient] = {}
_instance: HTTPClientPool | None = None
_default_client: httpx.AsyncClient | None = None
_clients: dict[str, httpx.AsyncClient] = {}
# 代理客户端缓存:{cache_key: (client, last_used_time)}
_proxy_clients: Dict[str, Tuple[httpx.AsyncClient, float]] = {}
_proxy_clients: dict[str, tuple[httpx.AsyncClient, float]] = {}
# 代理客户端缓存上限(避免内存泄漏)
_max_proxy_clients: int = 50
@@ -242,7 +244,7 @@ class HTTPClientPool:
@classmethod
async def get_proxy_client(
cls,
proxy_config: Optional[Dict[str, Any]] = None,
proxy_config: dict[str, Any] | None = None,
) -> httpx.AsyncClient:
"""
获取代理客户端(带缓存复用)
@@ -280,7 +282,7 @@ class HTTPClientPool:
await cls._evict_lru_proxy_client()
# 创建新客户端(使用默认超时,请求时可覆盖)
client_config: Dict[str, Any] = {
client_config: dict[str, Any] = {
"http2": False,
"verify": get_ssl_context(),
"follow_redirects": True,
@@ -370,8 +372,8 @@ class HTTPClientPool:
@classmethod
def create_client_with_proxy(
cls,
proxy_config: Optional[Dict[str, Any]] = None,
timeout: Optional[httpx.Timeout] = None,
proxy_config: dict[str, Any] | None = None,
timeout: httpx.Timeout | None = None,
**kwargs: Any,
) -> httpx.AsyncClient:
"""
@@ -387,7 +389,7 @@ class HTTPClientPool:
Returns:
配置好的 httpx.AsyncClient 实例(调用者需要负责关闭)
"""
client_config: Dict[str, Any] = {
client_config: dict[str, Any] = {
"http2": False,
"verify": get_ssl_context(),
"follow_redirects": True,
@@ -413,7 +415,7 @@ class HTTPClientPool:
return httpx.AsyncClient(**client_config)
@classmethod
def get_pool_stats(cls) -> Dict[str, Any]:
def get_pool_stats(cls) -> dict[str, Any]:
"""获取连接池统计信息"""
return {
"default_client_active": cls._default_client is not None,

View File

@@ -9,10 +9,11 @@
- 调用方可以根据状态决定降级策略
"""
from __future__ import annotations
import os
import time
from enum import Enum
from typing import Optional
import redis.asyncio as aioredis
from src.core.logger import logger
@@ -35,8 +36,8 @@ class RedisClientManager:
提供 Redis 连接管理、熔断器保护和状态监控。
"""
_instance: Optional["RedisClientManager"] = None
_redis: Optional[aioredis.Redis] = None
_instance: RedisClientManager | None = None
_redis: aioredis.Redis | None = None
def __new__(cls):
"""单例模式"""
@@ -50,11 +51,11 @@ class RedisClientManager:
return
self._initialized = True
self._circuit_open_until: Optional[float] = None
self._circuit_open_until: float | None = None
self._consecutive_failures: int = 0
self._circuit_threshold = int(os.getenv("REDIS_CIRCUIT_BREAKER_THRESHOLD", "3"))
self._circuit_reset_seconds = int(os.getenv("REDIS_CIRCUIT_BREAKER_RESET_SECONDS", "60"))
self._last_error: Optional[str] = None # 记录最后一次错误
self._last_error: str | None = None # 记录最后一次错误
def get_state(self) -> RedisState:
"""
@@ -100,7 +101,7 @@ class RedisClientManager:
self._consecutive_failures = 0
self._last_error = None
async def initialize(self, require_redis: bool = False) -> Optional[aioredis.Redis]:
async def initialize(self, require_redis: bool = False) -> aioredis.Redis | None:
"""
初始化Redis连接
@@ -236,7 +237,7 @@ class RedisClientManager:
self._redis = None
logger.info("全局Redis客户端已关闭")
def get_client(self) -> Optional[aioredis.Redis]:
def get_client(self) -> aioredis.Redis | None:
"""
获取Redis客户端非异步
@@ -249,10 +250,10 @@ class RedisClientManager:
# 全局单例
_redis_manager: Optional[RedisClientManager] = None
_redis_manager: RedisClientManager | None = None
async def get_redis_client(require_redis: bool = False) -> Optional[aioredis.Redis]:
async def get_redis_client(require_redis: bool = False) -> aioredis.Redis | None:
"""
获取全局Redis客户端
@@ -277,7 +278,7 @@ async def get_redis_client(require_redis: bool = False) -> Optional[aioredis.Red
return _redis_manager.get_client()
def get_redis_client_sync() -> Optional[aioredis.Redis]:
def get_redis_client_sync() -> aioredis.Redis | None:
"""
同步获取Redis客户端不会初始化