Archived
为什么:上一次分包重构把 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/ 指纹三项一致
31 lines
1.5 KiB
Python
31 lines
1.5 KiB
Python
# -*- 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)
|