mirror of
https://github.com/fawney19/Aether.git
synced 2026-09-02 17:30:23 +08:00
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:
@@ -4,11 +4,13 @@
|
||||
提供在异步上下文中安全执行同步函数的工具,避免阻塞事件循环。
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import asyncio
|
||||
from functools import partial, wraps
|
||||
from typing import Any, Callable, Coroutine, TypeVar
|
||||
from typing import Any, TypeVar
|
||||
|
||||
from collections.abc import Callable
|
||||
from collections.abc import Coroutine
|
||||
|
||||
T = TypeVar("T")
|
||||
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
"""
|
||||
|
||||
import hashlib
|
||||
from typing import Optional
|
||||
|
||||
from fastapi import Depends, Header, HTTPException, status
|
||||
from fastapi.security import HTTPAuthorizationCredentials, HTTPBearer
|
||||
@@ -117,7 +116,7 @@ async def get_current_user(
|
||||
|
||||
|
||||
async def get_current_user_from_header(
|
||||
authorization: Optional[str] = Header(None), db: Session = Depends(get_db)
|
||||
authorization: str | None = Header(None), db: Session = Depends(get_db)
|
||||
) -> User:
|
||||
"""
|
||||
从Header中获取当前用户(兼容性函数)
|
||||
|
||||
@@ -2,7 +2,9 @@
|
||||
|
||||
import functools
|
||||
import json
|
||||
from typing import Any, Callable
|
||||
from typing import Any
|
||||
|
||||
from collections.abc import Callable
|
||||
|
||||
from src.core.logger import logger
|
||||
|
||||
|
||||
@@ -6,10 +6,10 @@
|
||||
|
||||
import gzip
|
||||
import json
|
||||
from typing import Any, Optional
|
||||
from typing import Any
|
||||
|
||||
|
||||
def compress_json(data: Any) -> Optional[bytes]:
|
||||
def compress_json(data: Any) -> bytes | None:
|
||||
"""
|
||||
将JSON数据压缩为gzip格式的字节
|
||||
|
||||
@@ -33,7 +33,7 @@ def compress_json(data: Any) -> Optional[bytes]:
|
||||
return None
|
||||
|
||||
|
||||
def decompress_json(compressed_data: Optional[bytes]) -> Optional[Any]:
|
||||
def decompress_json(compressed_data: bytes | None) -> Any | None:
|
||||
"""
|
||||
解压gzip格式的字节为JSON数据
|
||||
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
提供统一的HTTP请求信息提取功能
|
||||
"""
|
||||
|
||||
from typing import Optional
|
||||
|
||||
from fastapi import Request
|
||||
|
||||
@@ -61,7 +60,7 @@ def get_user_agent(request: Request) -> str:
|
||||
return request.headers.get("User-Agent", "unknown")
|
||||
|
||||
|
||||
def get_request_id(request: Request) -> Optional[str]:
|
||||
def get_request_id(request: Request) -> str | None:
|
||||
"""
|
||||
获取请求ID(如果存在)
|
||||
|
||||
|
||||
@@ -1,6 +1,3 @@
|
||||
from typing import Dict, List, Optional, Union
|
||||
|
||||
|
||||
class SSEEventParser:
|
||||
"""轻量SSE解析器,按行接收输入并输出完整事件。"""
|
||||
|
||||
@@ -8,14 +5,14 @@ class SSEEventParser:
|
||||
self._reset_buffer()
|
||||
|
||||
def _reset_buffer(self) -> None:
|
||||
self._buffer: Dict[str, Union[Optional[str], List[str]]] = {
|
||||
self._buffer: dict[str, str | None | list[str]] = {
|
||||
"event": None,
|
||||
"data": [],
|
||||
"id": None,
|
||||
"retry": None,
|
||||
}
|
||||
|
||||
def _finalize_event(self) -> Optional[Dict[str, Optional[str]]]:
|
||||
def _finalize_event(self) -> dict[str, str | None] | None:
|
||||
data_lines = self._buffer.get("data", [])
|
||||
if not isinstance(data_lines, list) or not data_lines:
|
||||
self._reset_buffer()
|
||||
@@ -25,7 +22,7 @@ class SSEEventParser:
|
||||
event_val = self._buffer.get("event")
|
||||
id_val = self._buffer.get("id")
|
||||
retry_val = self._buffer.get("retry")
|
||||
event: Dict[str, Optional[str]] = {
|
||||
event: dict[str, str | None] = {
|
||||
"event": event_val if isinstance(event_val, str) else None,
|
||||
"data": data_str,
|
||||
"id": id_val if isinstance(id_val, str) else None,
|
||||
@@ -35,11 +32,11 @@ class SSEEventParser:
|
||||
self._reset_buffer()
|
||||
return event
|
||||
|
||||
def feed_line(self, line: Optional[str]) -> List[Dict[str, Optional[str]]]:
|
||||
def feed_line(self, line: str | None) -> list[dict[str, str | None]]:
|
||||
"""处理单行SSE文本,返回所有完成的事件。"""
|
||||
|
||||
normalized_line = (line or "").rstrip("\r")
|
||||
events: List[Dict[str, Optional[str]]] = []
|
||||
events: list[dict[str, str | None]] = []
|
||||
|
||||
# 空行表示事件结束
|
||||
if normalized_line == "":
|
||||
@@ -98,7 +95,7 @@ class SSEEventParser:
|
||||
self._append_data_line(normalized_line)
|
||||
return events
|
||||
|
||||
def flush(self) -> List[Dict[str, Optional[str]]]:
|
||||
def flush(self) -> list[dict[str, str | None]]:
|
||||
"""在流结束时调用,输出尚未完成的事件。"""
|
||||
|
||||
event = self._finalize_event()
|
||||
|
||||
@@ -9,12 +9,10 @@
|
||||
- 多实例部署:设置 SINGLE_INSTANCE_MODE=false 禁用启动清理
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import os
|
||||
import pathlib
|
||||
import uuid
|
||||
from typing import Dict, Optional
|
||||
|
||||
from src.core.logger import logger
|
||||
|
||||
@@ -31,10 +29,10 @@ class StartupTaskCoordinator:
|
||||
# 注意:这在 fork 模式下每个 worker 都是独立的
|
||||
_startup_cleanup_attempted = False
|
||||
|
||||
def __init__(self, redis_client=None, lock_dir: Optional[str] = None):
|
||||
def __init__(self, redis_client=None, lock_dir: str | None = None):
|
||||
self.redis = redis_client
|
||||
self._tokens: Dict[str, str] = {}
|
||||
self._file_handles: Dict[str, object] = {}
|
||||
self._tokens: dict[str, str] = {}
|
||||
self._file_handles: dict[str, object] = {}
|
||||
self._lock_dir = pathlib.Path(lock_dir or os.getenv("TASK_LOCK_DIR", "./.locks"))
|
||||
if not self._lock_dir.exists():
|
||||
self._lock_dir.mkdir(parents=True, exist_ok=True)
|
||||
@@ -44,7 +42,7 @@ class StartupTaskCoordinator:
|
||||
def _redis_key(self, name: str) -> str:
|
||||
return f"task_lock:{name}"
|
||||
|
||||
async def acquire(self, name: str, ttl: Optional[int] = None) -> bool:
|
||||
async def acquire(self, name: str, ttl: int | None = None) -> bool:
|
||||
ttl = ttl or int(os.getenv("TASK_COORDINATOR_LOCK_TTL", "86400"))
|
||||
|
||||
if self.redis:
|
||||
@@ -142,7 +140,7 @@ class StartupTaskCoordinator:
|
||||
return False
|
||||
|
||||
|
||||
async def ensure_singleton_task(name: str, redis_client=None, ttl: Optional[int] = None):
|
||||
async def ensure_singleton_task(name: str, redis_client=None, ttl: int | None = None):
|
||||
"""便捷协程,返回 (coordinator, acquired)"""
|
||||
|
||||
coordinator = StartupTaskCoordinator(redis_client)
|
||||
|
||||
@@ -6,7 +6,9 @@
|
||||
|
||||
import asyncio
|
||||
from functools import wraps
|
||||
from typing import Any, Callable, Optional, TypeVar
|
||||
from typing import Any, TypeVar
|
||||
|
||||
from collections.abc import Callable
|
||||
|
||||
from src.core.logger import logger
|
||||
|
||||
@@ -23,7 +25,7 @@ class AsyncTimeoutError(TimeoutError):
|
||||
self.timeout = timeout
|
||||
|
||||
|
||||
def with_timeout(seconds: float, operation_name: Optional[str] = None):
|
||||
def with_timeout(seconds: float, operation_name: str | None = None):
|
||||
"""
|
||||
装饰器:为异步函数添加超时保护
|
||||
|
||||
@@ -47,7 +49,7 @@ def with_timeout(seconds: float, operation_name: Optional[str] = None):
|
||||
op_name = operation_name or func.__name__
|
||||
try:
|
||||
return await asyncio.wait_for(func(*args, **kwargs), timeout=seconds)
|
||||
except asyncio.TimeoutError:
|
||||
except TimeoutError:
|
||||
logger.warning(f"操作超时: {op_name} (timeout={seconds}s)")
|
||||
raise AsyncTimeoutError(
|
||||
f"{op_name} 操作超时({seconds}秒)",
|
||||
@@ -89,7 +91,7 @@ async def run_with_timeout(
|
||||
"""
|
||||
try:
|
||||
return await asyncio.wait_for(coro, timeout=timeout)
|
||||
except asyncio.TimeoutError:
|
||||
except TimeoutError:
|
||||
logger.warning(f"操作超时: {operation_name} (timeout={timeout}s)")
|
||||
if raise_on_timeout:
|
||||
raise AsyncTimeoutError(
|
||||
@@ -113,7 +115,7 @@ class TimeoutContext:
|
||||
def __init__(self, timeout: float, operation_name: str = "operation"):
|
||||
self.timeout = timeout
|
||||
self.operation_name = operation_name
|
||||
self._task: Optional[asyncio.Task] = None
|
||||
self._task: asyncio.Task | None = None
|
||||
|
||||
async def __aenter__(self):
|
||||
return self
|
||||
@@ -172,7 +174,7 @@ async def read_first_chunk_with_ttfb_timeout(
|
||||
try:
|
||||
first_chunk = await asyncio.wait_for(aiter.__anext__(), timeout=timeout)
|
||||
return first_chunk, aiter
|
||||
except asyncio.TimeoutError:
|
||||
except TimeoutError:
|
||||
# 完整的资源清理:先关闭迭代器,再关闭底层响应
|
||||
await _cleanup_iterator_resources(aiter, request_id)
|
||||
logger.warning(
|
||||
|
||||
@@ -7,7 +7,10 @@
|
||||
import functools
|
||||
import inspect
|
||||
from contextlib import contextmanager
|
||||
from typing import Any, Callable, Generator, Optional
|
||||
from typing import Any
|
||||
|
||||
from collections.abc import Callable
|
||||
from collections.abc import Generator
|
||||
|
||||
from sqlalchemy.exc import DatabaseError, IntegrityError
|
||||
from sqlalchemy.orm import Session
|
||||
@@ -22,7 +25,7 @@ class TransactionError(Exception):
|
||||
pass
|
||||
|
||||
|
||||
def _find_db_session(args, kwargs) -> Optional[Session]:
|
||||
def _find_db_session(args, kwargs) -> Session | None:
|
||||
"""从参数中查找数据库会话"""
|
||||
# 从位置参数中查找Session
|
||||
for arg in args:
|
||||
@@ -152,8 +155,8 @@ def transaction_scope(
|
||||
db: Session,
|
||||
commit_on_success: bool = True,
|
||||
rollback_on_error: bool = True,
|
||||
operation_name: Optional[str] = None,
|
||||
) -> Generator[Session, None, None]:
|
||||
operation_name: str | None = None,
|
||||
) -> Generator[Session]:
|
||||
"""
|
||||
事务上下文管理器
|
||||
|
||||
|
||||
Reference in New Issue
Block a user