# -*- coding: utf-8 -*- r"""算法服务的**依赖兜底**:离线目标机没有网、`wheels/` 里也没有 FastAPI 的轮子 ⇒ 用随包的 `vendor/pyfastapi/`。 实逮(2026-09-29):开发机的 `.venv` 是 `include-system-site-packages=true`,fastapi/uvicorn 实际来自 系统 Python;所以"能跑"不等于"随包能跑"。本模块在 `import fastapi` 失败时把 `vendor/pyfastapi` 插进 `sys.path`,让服务在**纯 stdlib + 随包依赖**的环境下也能起来。 """ from __future__ import annotations import pathlib import sys try: from app_common.app_common_guanlan.api import install_root as _install_root except ImportError: # 包结构异常时按位置上跳兜底 from pathlib import Path as _P def _install_root(_f): # type: ignore[no-redef] return _P(_f).resolve().parents[4] def vendor_dir() -> pathlib.Path: return _install_root(__file__) / 'vendor' / 'pyfastapi' def ensure_fastapi() -> tuple[object, object]: """→ (fastapi, uvicorn);缺依赖时依次尝试:本地已装 → vendor/ 随包。""" d = vendor_dir() if d.is_dir() and str(d) not in sys.path: sys.path.append(str(d)) # 放在最后:已装的优先 try: import fastapi import uvicorn return fastapi, uvicorn except ImportError as e: # noqa: BLE001 raise SystemExit( f'[X] 算法服务缺运行期依赖: {e}\n' f' 随包目录: {d}(存在: {d.is_dir()})\n' ' 修法: 在有网机器上跑 python scripts/fetch_offline_deps.py all 把 fastapi/uvicorn 打进 ' 'wheels/,或把 site-packages 里的 fastapi·uvicorn·pydantic·starlette·anyio·h11·sniffio·' 'annotated_types·typing_extensions·typing_inspection·annotated_doc·idna·click·colorama 复制到 ' f'{d}') __all__ = ['ensure_fastapi', 'vendor_dir']