build(deps): 固化 Python 依赖版本,新增 lock 与生成脚本
为便携 release 打包与可复现安装做准备(P3 依赖固化)。 - 新增 requirements.lock.txt:锁定 9 个运行时直接依赖的精确版本 - requirements.txt 顶部加注释,说明与 lock 文件的分工(宽松 vs 精确) - 新增 scripts/gen_requirements_lock.py:从已验证环境一键重生 lock, 避免手工对照版本出错 调查结论(已核实): - 项目源码真实第三方依赖与 requirements.txt 完全一致,无遗漏 - tiktoken 已不再使用(token 统计改用 API usage 字段),无需纳入 - 三个 node 项目的 package-lock.json 均已存在并被 git 追踪,npm ci 可直接用,node 端无需改动 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
b8a51c1c63
commit
1d3703b0e5
20
requirements.lock.txt
Normal file
20
requirements.lock.txt
Normal file
@ -0,0 +1,20 @@
|
||||
# 锁定版本依赖(构建 / release 打包用)。由 scripts/gen_requirements_lock.py 生成。
|
||||
#
|
||||
# 与 requirements.txt 的关系:
|
||||
# - requirements.txt 人读、宽松,列出运行时直接依赖(不锁版本)
|
||||
# - requirements.lock.txt(本文件) 精确版本,供便携 release 打包与可复现安装
|
||||
#
|
||||
# 安装:pip install -r requirements.lock.txt
|
||||
# 更新:在已验证可用的环境执行 python -m scripts.gen_requirements_lock
|
||||
#
|
||||
# 注:这里只锁项目运行时直接依赖;其传递依赖由 pip 在安装时解析。
|
||||
|
||||
flask==2.3.3
|
||||
flask-socketio==5.3.6
|
||||
flask-cors==3.0.10
|
||||
werkzeug==2.3.7
|
||||
httpx==0.28.1
|
||||
openai==2.8.1
|
||||
cryptography==43.0.3
|
||||
pillow==9.3.0
|
||||
websockets==10.4
|
||||
@ -1,3 +1,5 @@
|
||||
# 运行时直接依赖(人读、宽松,不锁版本,便于开发期升级)。
|
||||
# 可复现安装 / release 打包请用 requirements.lock.txt(精确版本)。
|
||||
flask
|
||||
flask-socketio
|
||||
flask-cors
|
||||
|
||||
83
scripts/gen_requirements_lock.py
Normal file
83
scripts/gen_requirements_lock.py
Normal file
@ -0,0 +1,83 @@
|
||||
#!/usr/bin/env python3
|
||||
"""根据 requirements.txt 生成 requirements.lock.txt(精确版本)。
|
||||
|
||||
读取 requirements.txt 列出的直接依赖名,从当前已安装环境查出精确版本,
|
||||
写出 requirements.lock.txt。在「已验证可用」的环境中运行,确保锁定的是
|
||||
一组实测能跑起来的版本。
|
||||
|
||||
用法:
|
||||
python -m scripts.gen_requirements_lock
|
||||
python scripts/gen_requirements_lock.py
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import importlib.metadata as md
|
||||
import re
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
REPO_ROOT = Path(__file__).resolve().parents[1]
|
||||
REQ_PATH = REPO_ROOT / "requirements.txt"
|
||||
LOCK_PATH = REPO_ROOT / "requirements.lock.txt"
|
||||
|
||||
_LOCK_HEADER = """\
|
||||
# 锁定版本依赖(构建 / release 打包用)。由 scripts/gen_requirements_lock.py 生成。
|
||||
#
|
||||
# 与 requirements.txt 的关系:
|
||||
# - requirements.txt 人读、宽松,列出运行时直接依赖(不锁版本)
|
||||
# - requirements.lock.txt(本文件) 精确版本,供便携 release 打包与可复现安装
|
||||
#
|
||||
# 安装:pip install -r requirements.lock.txt
|
||||
# 更新:在已验证可用的环境执行 python -m scripts.gen_requirements_lock
|
||||
#
|
||||
# 注:这里只锁项目运行时直接依赖;其传递依赖由 pip 在安装时解析。
|
||||
"""
|
||||
|
||||
|
||||
def _parse_requirement_names(text: str) -> list[str]:
|
||||
"""从 requirements.txt 提取包名(忽略注释、空行、已带版本约束的尾巴)。"""
|
||||
names: list[str] = []
|
||||
for raw in text.splitlines():
|
||||
line = raw.strip()
|
||||
if not line or line.startswith("#"):
|
||||
continue
|
||||
# 去掉版本约束/extras,仅留包名: flask[async]>=2 -> flask
|
||||
name = re.split(r"[<>=!~\[ ;]", line, 1)[0].strip()
|
||||
if name:
|
||||
names.append(name)
|
||||
return names
|
||||
|
||||
|
||||
def main() -> int:
|
||||
if not REQ_PATH.exists():
|
||||
print(f"找不到 {REQ_PATH}", file=sys.stderr)
|
||||
return 1
|
||||
names = _parse_requirement_names(REQ_PATH.read_text(encoding="utf-8"))
|
||||
if not names:
|
||||
print("requirements.txt 中没有可锁定的依赖。", file=sys.stderr)
|
||||
return 1
|
||||
|
||||
lines: list[str] = []
|
||||
missing: list[str] = []
|
||||
for name in names:
|
||||
try:
|
||||
version = md.version(name)
|
||||
lines.append(f"{name}=={version}")
|
||||
except md.PackageNotFoundError:
|
||||
missing.append(name)
|
||||
lines.append(f"# {name} (未安装,无法锁定版本)")
|
||||
|
||||
LOCK_PATH.write_text(_LOCK_HEADER + "\n" + "\n".join(lines) + "\n", encoding="utf-8")
|
||||
print(f"已写出 {LOCK_PATH}({len(names)} 项依赖)")
|
||||
for line in lines:
|
||||
print(" " + line)
|
||||
if missing:
|
||||
print(f"\n警告:以下依赖未安装,未能锁定版本:{', '.join(missing)}", file=sys.stderr)
|
||||
print("请在已安装全部依赖的环境中重新运行。", file=sys.stderr)
|
||||
return 2
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
||||
Loading…
Reference in New Issue
Block a user