This repository has been archived on 2026-10-02. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
NeteaseUrl/services/__init__.py
T
snowy-yang 7a70eef636 refactor: 删除 services.runtime 兼容门面,7 处调用点直连真实子模块
为什么:上一次分包重构把 1900 行巨石按职责拆成 9 个子模块后,留了一个
runtime.py 作为「原样再导出」的兼容门面,好让调用点零改动。但迁移期结束后
没有一个调用点迁走——门面成了永久中间层:每次读代码都要先跳一层才知道
符号真正住在哪,pyright 也无法从门面追溯定义位置。

改法:保持「函数内延迟导入」的形式不变,只把导入目标换成真实子模块。
**刻意不提到模块顶部**——services/__init__.py 里有依赖自检
(缺 auth_system/podcast_api/music_api/music_downloader/qr_login 就 sys.exit(1)),
提到顶层会让「import web.*」这一步就触发该自检,改变启动语义,超出本次范围。

7 处调用点(AST 精确扫描确认全集):
- web/routers/download.py ×4:download / download_batch_zip / podcast_download /
  podcast_batch_zip,分别直连 services.downloads / batch / podcast / tasks
  外加 _shared._build_download_base 与 api_service.api_service
- web/routers/tasks.py ×2:_semaphores 取 _shared.BATCH_ZIP_SEM + api_service,
  topen 取 _shared.ENDPOINT_NAMES
- web/tasks_registry.py ×1:start_task 直连 _taskcore._start_task_thread

子模块短名统一加 `services_` 前缀别名(services_tasks 等),避免与函数内
既有局部名相撞。

顺带清理:9 个子模块 docstring 里「(自 services/runtime.py 拆出……)」的
历史说明在门面删除后已成悬空引用,一并去掉;包的模块划分说明改为
「不再有兼容门面层」。

验证:
- 符号可达:11 个被引符号在真实子模块上全部 hasattr 通过;
  services.runtime 已 ModuleNotFoundError;api_service 仍是同一实例对象
- 三个调用方模块均可正常 import
- 门禁:ruff 0 · pyright 0 errors 0 warnings · pytest 175 passed
- 真实 uvicorn 冒烟 8/8(含 POST /auth/login 400),临时数据目录独立落库
  9 张表,生产 data/ 指纹三项一致
2026-09-26 14:48:05 +08:00

31 lines
1.5 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# -*- coding: utf-8 -*-
"""服务层:从原 Flask 巨石中剥离的框架无关实现(不含任何 Flask 依赖)。
模块划分(原先集中在单个 1900 行模块,现按职责拆分;调用方直接
``from services import <子模块>``,不再有兼容门面层):
- ``api_service``:MusicAPIService 与进程级实例(config / api_service)
- ``_shared``:限流桶、请求统计、命名与共享常量
- ``_taskcore``:任务生命周期原语(启动 / 终态 / 文件收尾 / 结算)
- ``downloads``:单曲下载、账号探测与任务交付
- ``batch``:歌单/专辑批量打包 worker
- ``podcast``:播客单集下载与整台打包 worker
- ``streaming``:同步流式响应垫片(音频分段透传)
- ``tasks``:任务监控循环、设备心跳与缓存清扫
"""
# ============ 依赖自检 ============
# 应用依赖缺失时给出可读提示并非零退出。本层常以 NSSM 服务方式运行、控制台重定向为
# GBK,裸 traceback 反而更难定位,故提示串保持纯 ASCII。放在包初始化里是因为它必然
# 先于任何子模块执行——子模块各自 import 失败会先抛 traceback,那段提示就没机会出现。
import importlib
import sys
for _dep in ('auth_system', 'podcast_api', 'music_api', 'music_downloader', 'qr_login'):
try:
importlib.import_module(_dep)
except ImportError as _exc:
print("ImportError: " + str(_exc))
print("Please make sure all dependency modules exist and are importable.")
sys.exit(1)