Merge pull request #6619 from ranzhigg/fix/refresh-architecture-baseline

This commit is contained in:
jxxghp
2026-09-09 12:19:30 +08:00
committed by GitHub
7 changed files with 47 additions and 40 deletions

View File

@@ -26,10 +26,14 @@ from app.domain.plugin import (
is_local_plugin_source,
is_physical_plugin_id,
is_plugin_generation_compatible,
normalize_plugin_market_repo_url as _normalize_plugin_market_repo_url,
parse_local_plugin_generation,
parse_local_plugin_path,
parse_local_plugin_reference,
)
from app.domain.plugin import (
normalize_plugin_market_repo_url as _normalize_plugin_market_repo_url,
)
from app.domain.plugin import (
split_plugin_market_repo_urls as _split_plugin_market_repo_urls,
)
from app.foundation.environment import is_free_threaded_runtime
@@ -1157,7 +1161,7 @@ class PluginMarketTransport(metaclass=WeakSingleton):
def _remove_index_task(
cls,
key: tuple[asyncio.AbstractEventLoop, str, str],
task: asyncio.Task[Optional[PluginIndex]],
task: asyncio.Future[Optional[PluginIndex]],
) -> None:
"""异步索引请求结束后释放事件循环和仓库引用。"""
with cls._index_task_lock:
@@ -1219,10 +1223,14 @@ class PluginMarketTransport(metaclass=WeakSingleton):
key = self._plugin_index_key(repo_url, package_version)
with self._index_future_lock:
future = self._index_futures.get(key)
owner = future is None
if owner:
future = Future()
if future is None:
future = Future[Optional[PluginIndex]]()
self._index_futures[key] = future
owner = True
else:
owner = False
assert future is not None
if not owner:
return future.result()
@@ -1588,12 +1596,13 @@ class PluginMarketTransport(metaclass=WeakSingleton):
),
)
self._index_tasks[task_key] = task
task.add_done_callback(
lambda completed_task, key=task_key: self._remove_index_task(
key,
completed_task,
)
)
def on_index_task_done(
completed_task: asyncio.Future[Optional[PluginIndex]],
) -> None:
self._remove_index_task(task_key, completed_task)
task.add_done_callback(on_index_task_done)
# 单个调用方取消等待时不能连带取消共享请求。
return await asyncio.shield(task)

View File

@@ -470,16 +470,6 @@ class SiteChain(InteractionChainMixin, ChainBase):
return False, "Cookie已过期"
return False, f"错误:{res.status_code} {res.reason}"
@staticmethod
def __resolve_site_page_url(site_url: str, page_path: Optional[str]) -> str:
"""将站点资源声明的相对页面路径解析为绝对地址。"""
page_path = str(page_path or "").strip()
if not page_path:
return site_url
if page_path.startswith(("http://", "https://")):
return page_path
return urljoin(f"{str(site_url).rstrip('/')}/", page_path.lstrip("/"))
def __hddolby_test(self, site: SiteSnapshot) -> Tuple[bool, str]:
"""
判断站点是否已经登陆:hddolby
@@ -693,7 +683,7 @@ class SiteChain(InteractionChainMixin, ChainBase):
logger.warning(f"站点 {domain} 已在黑名单中,不添加站点")
return 0, 0, 0, False
domain_url = self._cookiecloud_indexer_domain(indexer, domain)
login_url = self.__resolve_site_page_url(domain_url, indexer.get("login_path"))
login_url = site_rules.resolve_page_url(domain_url, indexer.get("login_path"))
proxy, response = self._cookiecloud_connect(login_url, cookie, indexer)
if response is None:
return 0, 0, 1, False
@@ -886,16 +876,14 @@ class SiteChain(InteractionChainMixin, ChainBase):
state, message = special_test(site_info)
else:
indexer = SitesHelper().get_indexer(domain) or {}
login_path = indexer.get("login_path")
if login_path:
state, message = self.__test(
replace(
site_info,
url=self.__resolve_site_page_url(site_info.url, login_path),
)
state, message = self.__test(
replace(
site_info,
url=site_rules.resolve_page_url(
site_info.url, indexer.get("login_path")
),
)
else:
state, message = self.__test(site_info)
)
# 统计
seconds = (datetime.now() - start_time).seconds
if state:

View File

@@ -1,9 +1,11 @@
from typing import Optional
from urllib.parse import urljoin
from lxml import etree
from app.foundation.dom import DomUtils
from app.foundation.url import split_netloc
_SPECIAL_SITE_DOMAINS = (
"u2.dmhy.org",
"pt.ecust.pp.ua",
@@ -39,6 +41,16 @@ def extract_domain(url: str) -> str:
return ".".join(labels[-2:])
def resolve_page_url(site_url: str, page_path: Optional[str]) -> str:
"""将站点资源声明的页面路径解析为绝对地址。"""
page_path = str(page_path or "").strip()
if not page_path:
return site_url
if page_path.startswith(("http://", "https://")):
return page_path
return urljoin(f"{str(site_url).rstrip('/')}/", page_path.lstrip("/"))
class SiteUtils:
"""提供站点域名、Cookie 和访问参数处理能力。"""

View File

@@ -755,7 +755,7 @@ flowchart LR
| 指标 | 当前值 |
|---|---:|
| Python 模块 | 985 |
| 内部导入边 | 8,358 |
| 内部导入边 | 8,359 |
| 非平凡 SCC | 1(精确 containment 的 TMDB 移植包环) |
| Application / Chain 具体 Adapter 直连 | 0 / 0 |
| Direct egress | 53(债务已清零,53 条精确 containment) |

View File

@@ -94,7 +94,7 @@ ARCH-201 至 ARCH-204 均达到实现、验证、提交、推送和远端门禁
| 指标 | 当前值 | 解释 |
|---|---:|---|
| 宿主 Python 模块 / 内部依赖边 | 985 / 8,358 | `dependency-baseline.json` 当前快照;分类、下载资源归类、订阅搜索与整理任务恢复模块的受控依赖 |
| 宿主 Python 模块 / 内部依赖边 | 985 / 8,359 | `dependency-baseline.json` 当前快照;分类、下载资源归类、订阅搜索与整理任务恢复模块的受控依赖 |
| 非平凡 SCC | 1 | 仅保留精确 containment 的 29 模块 TMDB 移植包环 |
| 跨层 DB 边界债务 | 0 | Application、Chain、API、Agent、Runtime、Workflow 到 DB 的受控债务均为零 |
| Model/Oper 事务债务 | 0 | 自建 Session、自动事务装饰器、直接 commit/rollback 等基线均为零 |
@@ -103,7 +103,7 @@ ARCH-201 至 ARCH-204 均达到实现、验证、提交、推送和远端门禁
| Python 源码量 | 305,884 行 | 排除 `app/plugins/**`;61 个文件超过 1,000 行,11 个超过 2,000 行 |
| 长方法 | 290 个超过 80 行 | AST 统计排除 `app/plugins/**`;65 个超过 150 行,21 个超过 250 行 |
| 全量 mypy 历史债务 | 9,440 / 513 文件 | Agent API 重构后的现状基线;canonical Facade 与 endpoint 类型边界已补齐,低水位只允许继续下降 |
| Ruff 历史诊断 | 537 | 低水位门禁通过,但规则集只覆盖 `E4/E7/E9/F/I` |
| Ruff 历史诊断 | 536 | 低水位门禁通过,但规则集只覆盖 `E4/E7/E9/F/I` |
| 覆盖率固定基线 | Application 80.00%,Domain 80.00% | Chain、Runtime、Agent、Adapter、Startup 未进入包级覆盖率门禁 |
### 3.3 热点文件

View File

@@ -1074,8 +1074,8 @@
"runtime_only": true
}
},
"edge_count": 8358,
"edge_sha256": "c0785c61259ea746f28fa0e13d65fb72c3d0d438d52bc624177f0787f98f8728",
"edge_count": 8359,
"edge_sha256": "68e8ba94394d9c5b6e77890dde09b5f8916776c39992958460e198f854a1ee19",
"edges": [
"app -> app.foundation",
"app -> app.foundation.environment",
@@ -6617,6 +6617,7 @@
"app.modules.indexer -> app.modules.indexer.spider.yema",
"app.modules.indexer -> app.runtime",
"app.modules.indexer -> app.runtime.log",
"app.modules.indexer -> app.runtime.settings",
"app.modules.indexer -> app.schemas",
"app.modules.indexer -> app.schemas.media",
"app.modules.indexer -> app.schemas.site",

View File

@@ -167,9 +167,6 @@
"app/domain/scraper.py": {
"I001": 1
},
"app/domain/site.py": {
"I001": 1
},
"app/domain/title.py": {
"I001": 1
},