
cookiecutter 的 `cookiecutter.json` 是静态文件,不支持直接执行函数。可通过预处理脚本在运行 cookiecutter 前动态注入系统可用程序列表,实现运行时选择项生成。
在数据科学项目模板中,常需根据宿主系统已安装的外部工具(如 python、R、CUDA 或特定 CLI 工具)提供版本选择。由于这些选项依赖运行环境且变化频繁,硬编码在 cookiecutter.json 中既不灵活也不可维护——而该文件本身又不支持表达式或函数调用(如 “program_version”: [“generate_program_choices()”] 会被原样当作字符串处理,不会执行)。
✅ 正确做法:使用预生成脚本(pre-gen script) 动态更新配置文件。
步骤详解
- 编写 generate_program_choices.py(示例)
该脚本负责探测系统并返回合法选项列表(如已安装的 Python 版本):
# generate_program_choices.py import subprocess import re def get_python_versions(): try: result = subprocess.run(["pyenv", "versions", "--bare"], capture_output=True, text=True, check=True) versions = [v.strip() for v in result.stdout.splitlines() if v.strip()] # 过滤出语义化版本(如 3.9.18, 3.11.9) return sorted(set(v for v in versions if re.match(r"^d+.d+.d+$", v))) except (subprocess.CalledProcessError, FileNotFoundError): # 回退:尝试用 python --version + 系统查找 return ["3.9", "3.10", "3.11", "3.12"] if __name__ == "__main__": print("Available versions:", get_python_versions()) # 可选:直接输出供调试
- 编写 update_choices.py(预处理主脚本)
在运行 cookiecutter 前执行,自动重写 cookiecutter.json:
# update_choices.py import json import sys from pathlib import Path # 导入你的探测逻辑(确保在 PYTHONPATH 中或同目录) sys.path.insert(0, str(Path(__file__).parent)) from generate_program_choices import get_python_versions # 读取并更新 cookiecutter.json config_path = Path("cookiecutter.json") with config_path.open(encoding="utf-8") as f: config = json.load(f) # 动态注入选项 —— 注意:必须是 list 类型,且元素为字符串 config["program_version"] = get_python_versions() # 写回(保留格式与缩进) with config_path.open("w", encoding="utf-8") as f: json.dump(config, f, indent=4, ensure_ascii=False) print(f"✅ Updated 'program_version' with {len(config['program_version'])} options.")
- 执行流程(推荐封装为一键命令)
python update_choices.py && cookiecutter .
⚠️ 关键注意事项
- cookiecutter.json 中 “program_version” 字段值必须为字符串列表(如 [“3.11.9”, “3.12.3”]),不可为函数引用或空值;
- 若模板被多人协作或 CI 使用,建议将 update_choices.py 加入 .gitignore 的生成产物(但脚本本身应纳入版本控制);
- 如需更高阶控制(如条件跳过、交互式确认),可在脚本中加入 input() 或 argparse;
- 避免在 pre_gen_project.py 中修改 cookiecutter.json:该钩子在 cookiecutter 解析 JSON 之后 才运行,无法影响变量选择界面。
通过此方案,你既保持了 Cookiecutter 的声明式体验,又获得了运行时环境感知能力——简洁、可靠,且完全符合数据科学模板“一次生成、长期稳定”的设计目标。