mirror of
https://github.com/jxxghp/MoviePilot.git
synced 2026-08-28 19:47:41 +08:00
refactor: strengthen architecture CI gates and runtime contracts
This commit is contained in:
+30
-28
@@ -9,31 +9,32 @@ import sys
|
||||
import threading
|
||||
from asyncio import AbstractEventLoop
|
||||
from pathlib import Path
|
||||
from typing import Any, Dict, List, Optional, Tuple, Type, Union, get_origin, get_args
|
||||
from typing import Any, Dict, List, Optional, Tuple, Type, Union, get_args, get_origin
|
||||
from urllib.parse import quote, urlencode, urlparse
|
||||
|
||||
from dotenv import set_key, unset_key
|
||||
from pydantic import BaseModel, Field, ConfigDict, model_validator
|
||||
from pydantic import BaseModel, ConfigDict, Field, model_validator
|
||||
from pydantic_settings import BaseSettings, SettingsConfigDict
|
||||
|
||||
from app.foundation.environment import is_free_threaded_runtime
|
||||
from app.runtime.log import (
|
||||
LogConfigModel,
|
||||
configure_log_settings,
|
||||
configure_log_writer,
|
||||
logger,
|
||||
log_settings,
|
||||
NonBlockingFileHandler,
|
||||
)
|
||||
from app.schemas.types import MediaType
|
||||
from app.foundation.environment import (
|
||||
cpu_arch,
|
||||
get_env_path,
|
||||
is_docker,
|
||||
is_free_threaded_runtime,
|
||||
is_frozen,
|
||||
)
|
||||
from app.foundation.url import UrlUtils
|
||||
from app.runtime.log import (
|
||||
LogConfigModel,
|
||||
NonBlockingFileHandler,
|
||||
configure_log_settings,
|
||||
configure_log_writer,
|
||||
log_settings,
|
||||
logger,
|
||||
)
|
||||
from app.runtime.stop import runtime_stop_state
|
||||
from app.runtime.version import get_app_version
|
||||
from app.schemas.types import MediaType
|
||||
|
||||
|
||||
class SystemConfModel(BaseModel):
|
||||
@@ -1374,7 +1375,6 @@ class GlobalVar(object):
|
||||
"""
|
||||
|
||||
# 系统停止事件
|
||||
STOP_EVENT: threading.Event = threading.Event()
|
||||
# webpush订阅
|
||||
SUBSCRIPTIONS: List[dict] = []
|
||||
# webpush订阅读写锁
|
||||
@@ -1391,18 +1391,28 @@ class GlobalVar(object):
|
||||
self._event_loop_owners: dict[object, AbstractEventLoop] = {}
|
||||
self._event_loop_owner_lock = threading.Lock()
|
||||
|
||||
@property
|
||||
def STOP_EVENT(self) -> threading.Event:
|
||||
"""兼容旧代码读取进程停止事件。"""
|
||||
return runtime_stop_state.system_event
|
||||
|
||||
@STOP_EVENT.setter
|
||||
def STOP_EVENT(self, event: threading.Event) -> None:
|
||||
"""兼容旧测试替换事件,同时保持新 StopState 为唯一状态源。"""
|
||||
runtime_stop_state.replace_system_event(event)
|
||||
|
||||
def stop_system(self):
|
||||
"""
|
||||
停止系统
|
||||
"""
|
||||
self.STOP_EVENT.set()
|
||||
runtime_stop_state.stop_system()
|
||||
|
||||
@property
|
||||
def is_system_stopped(self):
|
||||
"""
|
||||
是否停止
|
||||
"""
|
||||
return self.STOP_EVENT.is_set()
|
||||
return runtime_stop_state.is_system_stopped
|
||||
|
||||
def get_subscriptions(self):
|
||||
"""
|
||||
@@ -1444,39 +1454,31 @@ class GlobalVar(object):
|
||||
"""
|
||||
停止工作流
|
||||
"""
|
||||
if workflow_id not in self.EMERGENCY_STOP_WORKFLOWS:
|
||||
self.EMERGENCY_STOP_WORKFLOWS.append(workflow_id)
|
||||
runtime_stop_state.stop_workflow(workflow_id)
|
||||
|
||||
def workflow_resume(self, workflow_id: int):
|
||||
"""
|
||||
恢复工作流
|
||||
"""
|
||||
if workflow_id in self.EMERGENCY_STOP_WORKFLOWS:
|
||||
self.EMERGENCY_STOP_WORKFLOWS.remove(workflow_id)
|
||||
runtime_stop_state.resume_workflow(workflow_id)
|
||||
|
||||
def is_workflow_stopped(self, workflow_id: int) -> bool:
|
||||
"""
|
||||
是否停止工作流
|
||||
"""
|
||||
return self.is_system_stopped or workflow_id in self.EMERGENCY_STOP_WORKFLOWS
|
||||
return runtime_stop_state.is_workflow_stopped(workflow_id)
|
||||
|
||||
def stop_transfer(self, path: str):
|
||||
"""
|
||||
停止文件整理
|
||||
"""
|
||||
if path not in self.EMERGENCY_STOP_TRANSFER:
|
||||
self.EMERGENCY_STOP_TRANSFER.append(path)
|
||||
runtime_stop_state.stop_transfer(path)
|
||||
|
||||
def is_transfer_stopped(self, path: str) -> bool:
|
||||
"""
|
||||
是否停止文件整理
|
||||
"""
|
||||
if self.is_system_stopped:
|
||||
return True
|
||||
if path in self.EMERGENCY_STOP_TRANSFER:
|
||||
self.EMERGENCY_STOP_TRANSFER.remove(path)
|
||||
return True
|
||||
return False
|
||||
return runtime_stop_state.consume_transfer_stop(path)
|
||||
|
||||
@property
|
||||
def loop(self) -> AbstractEventLoop:
|
||||
|
||||
@@ -0,0 +1,112 @@
|
||||
"""进程停止与细粒度取消信号契约。"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import threading
|
||||
from typing import Protocol
|
||||
|
||||
|
||||
class StopState(Protocol):
|
||||
"""向运行时消费者暴露系统、工作流和整理任务的停止状态。"""
|
||||
|
||||
@property
|
||||
def is_system_stopped(self) -> bool:
|
||||
"""返回系统是否已经进入停止阶段。"""
|
||||
...
|
||||
|
||||
def stop_system(self) -> None:
|
||||
"""发布不可逆的当前进程停止信号。"""
|
||||
...
|
||||
|
||||
def stop_workflow(self, workflow_id: int) -> None:
|
||||
"""请求停止指定工作流。"""
|
||||
...
|
||||
|
||||
def resume_workflow(self, workflow_id: int) -> None:
|
||||
"""清除指定工作流的停止请求。"""
|
||||
...
|
||||
|
||||
def is_workflow_stopped(self, workflow_id: int) -> bool:
|
||||
"""返回系统或指定工作流是否已经停止。"""
|
||||
...
|
||||
|
||||
def stop_transfer(self, path: str) -> None:
|
||||
"""登记指定源路径的一次性整理停止请求。"""
|
||||
...
|
||||
|
||||
def consume_transfer_stop(self, path: str) -> bool:
|
||||
"""消费指定路径的停止请求;系统停止时始终返回真。"""
|
||||
...
|
||||
|
||||
|
||||
class ProcessStopState:
|
||||
"""线程安全地持有当前进程的停止与细粒度取消状态。"""
|
||||
|
||||
def __init__(self) -> None:
|
||||
self._system_event = threading.Event()
|
||||
self._workflow_ids: set[int] = set()
|
||||
self._transfer_paths: set[str] = set()
|
||||
self._lock = threading.Lock()
|
||||
|
||||
@property
|
||||
def system_event(self) -> threading.Event:
|
||||
"""返回兼容入口使用的系统停止事件。"""
|
||||
return self._system_event
|
||||
|
||||
def replace_system_event(self, event: threading.Event) -> None:
|
||||
"""替换系统事件,仅供旧 ABI 与隔离测试继续使用。"""
|
||||
self._system_event = event
|
||||
|
||||
@property
|
||||
def is_system_stopped(self) -> bool:
|
||||
"""返回系统是否已经进入停止阶段。"""
|
||||
return self._system_event.is_set()
|
||||
|
||||
def stop_system(self) -> None:
|
||||
"""发布不可逆的当前进程停止信号。"""
|
||||
self._system_event.set()
|
||||
|
||||
def stop_workflow(self, workflow_id: int) -> None:
|
||||
"""请求停止指定工作流。"""
|
||||
with self._lock:
|
||||
self._workflow_ids.add(workflow_id)
|
||||
|
||||
def resume_workflow(self, workflow_id: int) -> None:
|
||||
"""清除指定工作流的停止请求。"""
|
||||
with self._lock:
|
||||
self._workflow_ids.discard(workflow_id)
|
||||
|
||||
def is_workflow_stopped(self, workflow_id: int) -> bool:
|
||||
"""返回系统或指定工作流是否已经停止。"""
|
||||
if self.is_system_stopped:
|
||||
return True
|
||||
with self._lock:
|
||||
return workflow_id in self._workflow_ids
|
||||
|
||||
def stop_transfer(self, path: str) -> None:
|
||||
"""登记指定源路径的一次性整理停止请求。"""
|
||||
with self._lock:
|
||||
self._transfer_paths.add(path)
|
||||
|
||||
def consume_transfer_stop(self, path: str) -> bool:
|
||||
"""消费指定路径的停止请求;系统停止时始终返回真。"""
|
||||
if self.is_system_stopped:
|
||||
return True
|
||||
with self._lock:
|
||||
if path not in self._transfer_paths:
|
||||
return False
|
||||
self._transfer_paths.remove(path)
|
||||
return True
|
||||
|
||||
def workflow_stop_ids(self) -> list[int]:
|
||||
"""返回旧诊断入口需要的工作流停止快照。"""
|
||||
with self._lock:
|
||||
return list(self._workflow_ids)
|
||||
|
||||
def transfer_stop_paths(self) -> list[str]:
|
||||
"""返回旧诊断入口需要的整理停止快照。"""
|
||||
with self._lock:
|
||||
return list(self._transfer_paths)
|
||||
|
||||
|
||||
runtime_stop_state = ProcessStopState()
|
||||
Reference in New Issue
Block a user