mirror of
https://github.com/jxxghp/MoviePilot.git
synced 2026-11-10 07:39:24 +08:00
Run `ruff check app tests scripts --fix` with the configured rule set (E4/E7/E9/F/I): import sorting (I001), f-strings without placeholders (F541), lambda/variable/unused-import cleanups that ruff marks safe. Nine files whose only ruff debt sits next to pre-existing pylint import-resolution errors (lazy exports, sys.path script imports) or whose "unused" imports are reached through attribute access by tests are left untouched so the changed-file pylint gate and the benchmark tests stay green; their diagnostics remain in the baseline. app/startup/lifecycle/__init__.py and app/main.py import business modules after cache composition, import-path and stdio setup on purpose (the source comments document the contract), so declare E402 per-file-ignores for them instead of adding per-line noqa. Ruff ratchet watermark drops from 498 to 100; sync the baseline fixture and the optimization checklist figure. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
60 lines
2.1 KiB
Python
60 lines
2.1 KiB
Python
"""
|
||
数据库错误诊断。
|
||
|
||
把驱动层的错误分类字段(sqlite3 / psycopg2 / asyncpg 各不相同)提取成统一结构,
|
||
并挂到引擎的异常事件上,使排障不依赖于阅读原始驱动异常。
|
||
"""
|
||
from typing import Any, Optional
|
||
|
||
from sqlalchemy import event
|
||
from sqlalchemy.engine import Engine as SQLAlchemyEngine
|
||
from sqlalchemy.engine import ExceptionContext
|
||
|
||
from app.runtime.log import logger
|
||
|
||
|
||
def _database_error_metadata(error: BaseException) -> Optional[dict[str, Any]]:
|
||
"""提取 SQLite 与 PostgreSQL 驱动提供的稳定错误分类字段。"""
|
||
metadata = {"error_type": type(error).__name__}
|
||
|
||
# DBAPI 驱动字段并不共享统一类型,动态读取可同时兼容 sqlite3、psycopg2 与 asyncpg。
|
||
sqlite_errorcode = getattr(error, "sqlite_errorcode", None)
|
||
sqlite_errorname = getattr(error, "sqlite_errorname", None)
|
||
if sqlite_errorcode is not None or sqlite_errorname:
|
||
if sqlite_errorcode is not None:
|
||
metadata["error_code"] = sqlite_errorcode
|
||
if sqlite_errorname:
|
||
metadata["error_name"] = sqlite_errorname
|
||
return metadata
|
||
|
||
sqlstate = getattr(error, "sqlstate", None) or getattr(error, "pgcode", None)
|
||
if not sqlstate:
|
||
sqlstate = getattr(getattr(error, "diag", None), "sqlstate", None)
|
||
if sqlstate:
|
||
metadata["sqlstate"] = sqlstate
|
||
return metadata
|
||
|
||
return None
|
||
|
||
|
||
def _log_database_error(exception_context: ExceptionContext) -> None:
|
||
"""记录非敏感驱动错误码,并保持 SQLAlchemy 原有异常传播。"""
|
||
metadata = _database_error_metadata(exception_context.original_exception)
|
||
if not metadata:
|
||
return
|
||
|
||
dialect = exception_context.dialect
|
||
fields = {
|
||
"database": dialect.name,
|
||
"driver": dialect.driver,
|
||
**metadata,
|
||
}
|
||
logger.error(
|
||
"数据库驱动异常:" + ", ".join(f"{key}={value}" for key, value in fields.items())
|
||
)
|
||
|
||
|
||
def _register_database_error_logging(engine: SQLAlchemyEngine) -> None:
|
||
"""为主程序 Engine 注册统一的底层驱动错误诊断。"""
|
||
event.listen(engine, "handle_error", _log_database_error)
|