test(architecture): lower parity type debt

This commit is contained in:
jxxghp
2026-08-29 22:07:38 +08:00
parent 094621f5b8
commit 4b1d4dd5b3
5 changed files with 70 additions and 17 deletions

View File

@@ -1,7 +1,7 @@
"""搜索处理链稳定 Facade。"""
from collections.abc import Callable
from typing import Any, TypeVar, cast
from typing import Any, Optional, TypeVar, cast
from app.chain.base import ChainBase
from app.chain.search.cache import SearchCacheOwner
@@ -15,8 +15,10 @@ from app.chain.search.result import SearchResultOwner
from app.chain.search.site import SearchSiteOwner
from app.chain.search.subtitle import SearchSubtitleOwner
from app.chain.search.title import SearchTitleOwner
from app.domain.context import Context, MediaInfo
from app.runtime.events import Event, eventmanager
from app.schemas.types import EventType
from app.schemas.mediaserver import NotExistMediaInfo
from app.schemas.types import EventType, MediaType
_PUBLIC_MODULE = "app.chain.search"
_Handler = TypeVar("_Handler", bound=Callable[..., Any])
@@ -78,7 +80,27 @@ class SearchChain(ChainBase):
_invoke_recommend_llm = staticmethod(SearchRecommendOwner._invoke_recommend_llm)
start_recommend_task = SearchRecommendOwner.start_recommend_task
search_by_id = SearchMediaOwner.search_by_id
search_by_title = SearchTitleOwner.search_by_title
def search_by_title(
self,
title: str,
page: Optional[int] = 0,
sites: Optional[list[int]] = None,
cache_local: Optional[bool] = False,
mtype: Optional[MediaType] = None,
rule_groups: Optional[list[str]] = None,
) -> list[Context]:
"""通过稳定 Facade 调用标题搜索 owner,保留精确的公开类型合同。"""
return SearchTitleOwner.search_by_title(
cast(SearchTitleOwner, self),
title=title,
page=page,
sites=sites,
cache_local=cache_local,
mtype=mtype,
rule_groups=rule_groups,
)
last_search_results = SearchCacheOwner.last_search_results
async_last_search_results = SearchCacheOwner.async_last_search_results
async_last_subtitle_search_results = SearchCacheOwner.async_last_subtitle_search_results
@@ -101,7 +123,31 @@ class SearchChain(ChainBase):
_process_music = SearchMusicOwner._process_music
_async_process_music = SearchMusicOwner._async_process_music
_async_process_music_stream = SearchMusicOwner._async_process_music_stream
process = SearchMediaOwner.process
def process(
self,
mediainfo: MediaInfo,
keyword: Optional[str] = None,
no_exists: Optional[dict[str, dict[int, NotExistMediaInfo]]] = None,
sites: Optional[list[int]] = None,
rule_groups: Optional[list[str]] = None,
area: Optional[str] = "title",
custom_words: Optional[list[str]] = None,
filter_params: Optional[dict[str, str]] = None,
) -> list[Context]:
"""通过稳定 Facade 调用精确媒体搜索 owner,保留公开类型合同。"""
return SearchMediaOwner.process(
cast(SearchMediaOwner, self),
mediainfo=mediainfo,
keyword=keyword,
no_exists=no_exists,
sites=sites,
rule_groups=rule_groups,
area=area,
custom_words=custom_words,
filter_params=filter_params,
)
async_process = SearchMediaOwner.async_process
async_process_stream = SearchMediaOwner.async_process_stream
_build_subtitle_season_episodes = staticmethod(SearchSubtitleOwner._build_subtitle_season_episodes)

View File

@@ -9,7 +9,7 @@ from app.chain._music import MusicSubscribeMixin
from app.chain.base import ChainBase
from app.chain.download import DownloadChain
from app.chain.media import MediaChain
from app.chain.search import SearchChain
from app.chain.search.facade import SearchChain
from app.chain.subscribe.completion import SubscribeCompletionOwner
from app.chain.subscribe.create import SubscribeCreateOwner
from app.chain.subscribe.interaction import SubscribeInteractionOwner
@@ -56,19 +56,22 @@ class SubscribeChain(
"""为音乐订阅 owner 提供可替换的媒体识别构造点。"""
from app.chain import _music as _music_mixin
return (_music_mixin.MediaChain or MediaChain)()
factory = getattr(_music_mixin, "MediaChain", None) or MediaChain
return factory()
def _music_download_chain(self) -> DownloadChain:
"""为音乐订阅 owner 提供可替换的下载构造点。"""
from app.chain import _music as _music_mixin
return (_music_mixin.DownloadChain or DownloadChain)()
factory = getattr(_music_mixin, "DownloadChain", None) or DownloadChain
return factory()
def _music_search_chain(self) -> SearchChain:
"""为音乐订阅 owner 提供可替换的搜索构造点。"""
from app.chain import _music as _music_mixin
return (_music_mixin.SearchChain or SearchChain)()
factory = getattr(_music_mixin, "SearchChain", None) or SearchChain
return factory()
def _music_site_keywords(self, mediainfo: MediaInfo) -> list[str]:
"""返回音乐订阅目标在各站点使用的检索关键字。"""

View File

@@ -78,7 +78,7 @@ MoviePilot V3 已经形成较清晰的模块化单体:`foundation`、`domain`
| Event Contract | 53 | 均已有 payload model,但当前全部是 diagnostic enforcement |
| Python 源码量 | 约 298,700 行 | 60 个文件超过 1,000 行,10 个超过 2,000 行 |
| 长方法 | 299 个超过 80 行 | 70 个超过 150 行,23 个超过 250 行;大量是私有方法 |
| 全量 mypy 历史债务 | 10,086 / 592 文件 | 识别链 typed plan/result、TMDB API、消息、Indexer、目录源与 Recommend 双 ABI 共用计划继续清除参数、返回值和泛型注解债;strict frontier 当前覆盖 41 个文件 |
| 全量 mypy 历史债务 | 10,063 / 592 文件 | 识别链 typed plan/result、TMDB API、消息、Indexer、目录源与 Recommend 双 ABI 共用计划继续清除参数、返回值和泛型注解债;strict frontier 当前覆盖 41 个文件 |
| Ruff 历史诊断 | 637 | 低水位门禁通过,但规则集只覆盖 `E4/E7/E9/F/I` |
| 覆盖率低水位 | Application 81.45%,Domain 81.01% | Chain、Runtime、Agent、Adapter、Startup 未进入包级覆盖率门禁 |

View File

@@ -1089,8 +1089,8 @@
"runtime_only": true
}
},
"edge_count": 8330,
"edge_sha256": "1a084d7f7953fed5f141f7bc6082442fffd69c0b71ad65a65b64e0454602c617",
"edge_count": 8336,
"edge_sha256": "7219becfe5cbd053de6d1779a5db3323218b0ca1893e354ef83d3dd3c77fa10a",
"edges": [
"app -> app.foundation",
"app -> app.foundation.environment",
@@ -4317,6 +4317,7 @@
"app.chain._music -> app.chain.download",
"app.chain._music -> app.chain.media",
"app.chain._music -> app.chain.search",
"app.chain._music -> app.chain.search.facade",
"app.chain._music -> app.domain",
"app.chain._music -> app.domain.context",
"app.chain._music -> app.domain.media",
@@ -4578,6 +4579,7 @@
"app.chain.interaction -> app.chain.download",
"app.chain.interaction -> app.chain.media",
"app.chain.interaction -> app.chain.search",
"app.chain.interaction -> app.chain.search.facade",
"app.chain.interaction -> app.chain.subscribe",
"app.chain.interaction -> app.chain.subscribe.facade",
"app.chain.interaction -> app.domain",
@@ -4891,9 +4893,12 @@
"app.chain.search.facade -> app.chain.search.site",
"app.chain.search.facade -> app.chain.search.subtitle",
"app.chain.search.facade -> app.chain.search.title",
"app.chain.search.facade -> app.domain",
"app.chain.search.facade -> app.domain.context",
"app.chain.search.facade -> app.runtime",
"app.chain.search.facade -> app.runtime.events",
"app.chain.search.facade -> app.schemas",
"app.chain.search.facade -> app.schemas.mediaserver",
"app.chain.search.facade -> app.schemas.types",
"app.chain.search.media -> app.chain",
"app.chain.search.media -> app.chain.media",
@@ -5127,6 +5132,7 @@
"app.chain.subscribe.facade -> app.chain.download",
"app.chain.subscribe.facade -> app.chain.media",
"app.chain.subscribe.facade -> app.chain.search",
"app.chain.subscribe.facade -> app.chain.search.facade",
"app.chain.subscribe.facade -> app.chain.subscribe",
"app.chain.subscribe.facade -> app.chain.subscribe.completion",
"app.chain.subscribe.facade -> app.chain.subscribe.create",

View File

@@ -1366,9 +1366,7 @@
"union-attr": 11
},
"app/chain/subscribe/facade.py": {
"attr-defined": 3,
"misc": 4,
"truthy-function": 1
"misc": 4
},
"app/chain/subscribe/interaction.py": {
"arg-type": 2
@@ -1913,9 +1911,9 @@
"assignment": 27,
"empty-body": 1,
"misc": 2,
"no-any-return": 8,
"no-untyped-call": 12,
"no-untyped-def": 11,
"no-any-return": 1,
"no-untyped-call": 4,
"no-untyped-def": 7,
"return-value": 2,
"type-arg": 13,
"union-attr": 2