Cookiecutter 嵌套配置文件(Nested Configuration Files)完全指南:用templates键构建模板层级选择体系
【免费下载链接】cookiecutterA cross-platform command-line utility that creates projects from cookiecutters (project templates), e.g. Python package projects, C projects.项目地址: https://gitcode.com/gh_mirrors/co/cookiecutter
导读
本指南以 Cookiecutter 官方文档 nested_config_files.rst 为主体,系统讲解如何在单个"主模板目录"中聚合多个子模板,并通过templates(新格式,2.5.0 起)与template(旧格式,2.2.0 起)两种键在运行时让用户交互选择。读完本文,你将掌握嵌套配置的目录组织方式、两种 JSON 配置格式的完整写法、交互式选择提示的运行机制,以及--no-input模式下的行为差异,并能结合源码理解其底层实现原理。
1. 什么是嵌套配置文件
Cookiecutter 本身以"一个cookiecutter.json+ 一个模板目录"为基本单元。但在实际工程中,团队往往需要在一个仓库里同时维护多个模板——例如一个project模板(生成完整项目骨架)和一个package模板(生成可发布的 Python 包),或者按技术栈拆分的多个变体。
嵌套配置文件(Nested Configuration Files)正是为此设计:在主目录的cookiecutter.json中声明一个templates(或旧版template)键,指向其他子模板目录。这样,运行cookiecutter时不再直接进入变量提问,而是先弹出一个模板选择菜单,选定后再进入对应子模板的cookiecutter.json提问流程。该功能由 prompt.py 中的choose_nested_template()与 main.py 中的递归调用共同实现。
2. 目录结构:一主多从的模板层级
假设我们想要在同一个主目录下聚合两个子模板,官方文档给出的推荐结构如下:
main-directory/ ├── project-1 │ ├── cookiecutter.json │ ├── {{cookiecutter.project_slug}} │ │ ├── ... ├── package │ ├── cookiecutter.json │ ├── {{cookiecutter.project_slug}} │ │ ├── ... └── cookiecutter.json关键点在于:
- 主配置文件位于主目录根(
main-directory/cookiecutter.json),它不包含实际的模板变量,只负责"分发"; - 每个子模板都是完整的 Cookiecutter 模板,各自拥有独立的
cookiecutter.json与{{cookiecutter.xxx}}渲染目录; - 子模板的路径在配置中以相对路径形式声明(如
./project-1),运行时以主目录为基准解析。
仓库中的真实测试夹具 fake-nested-templates 完整复现了这一结构,是学习该特性的最佳参考样例。
3. 新格式(推荐):templates键
自 Cookiecutter 2.5.0 起可用
在主cookiecutter.json中写入templates键,其值为一个对象,每个键对应一个子模板,值包含path、title、description三个字段:
{ "templates": { "project-1": { "path": "./project-1", "title": "Project 1", "description": "A cookiecutter template for a project" }, "package": { "path": "./package", "title": "Package", "description": "A cookiecutter template for a package" } } }3.1 字段语义与默认值
从源码 prompt.py 中_prompts_from_options()的实现可以精确推导每个字段的作用:
| 字段 | 含义 | 缺省行为(从源码看) |
|---|---|---|
path | 子模板相对于主目录的路径,必填 | 无默认值,缺失时取不到路径 |
title | 选择菜单中展示的短名称 | 缺省时回退为templates中的键名(如project-1) |
description | 选择菜单中展示的补充说明 | 缺省时与title取相同的回退值(键名) |
菜单标签的生成逻辑为:若title == description,直接显示该文本;否则组合为title (description)的格式。
3.2 仓库中的真实样例
fake-nested-templates/cookiecutter.json 是一个可直接套用的完整示例:
{ "templates": { "fake-project": { "path": "./fake-project", "title": "A Fake Project", "description": "A cookiecutter template for a project" }, "fake-package": { "path": "./fake-package", "title": "A Fake Package", "description": "A cookiecutter template for a package" } } }4. 交互式选择提示
在主目录中启动cookiecutter后,交互界面如下(官方文档原文):
Select template: 1 - Project 1 (A cookiecutter template for a project) 2 - Package (A cookiecutter template for a package) Choose from 1, 2 [1]:选择1后,Cookiecutter 会继续进入project-1子模板,按其cookiecutter.json依次询问变量(例如project-slug)。
4.1 提示文本从哪来
菜单提示由 prompt.py 中的prompt_choice_for_template()构建:
- 提示语固定为
Select a template(由_prompts_from_options()中的{"__prompt__": "Select a template"}定义); - 每个选项以
序号 - 标签形式渲染,标签即title与description的组合; - 默认选中第一项,提示行
Choose from 1, 2 [1]中的[1]表示回车直接采用默认值。
4.2--no-input模式
当使用--no-input(即no_input=True)运行时,不再弹出菜单,而是直接取templates对象中的第一个键所对应的子模板——对应 prompt.py 中的return opts[0] if no_input else ...。这也意味着:--no-input模式下无法选择第二个及以后的子模板,如需指定请把目标模板放在首位,或考虑使用--directory等替代方案。
5. 旧格式:template键(字符串列表)
自 Cookiecutter 2.2.0 起可用,2.5.0 起被templates键取代但仍受支持
旧格式在主cookiecutter.json中使用template键,其值为字符串数组,每个元素形如标题 (./相对路径):
{ "template": [ "Project 1 (./project-1)", "Project 2 (./project-2)" ] }交互提示效果与新格式等价:
Select template: 1 - Project 1 (./project-1) 2 - Project 2 (./project-2) Choose from 1, 2 [1]:5.1 旧格式的解析机制
旧格式没有path、title、description等结构化字段,路径需要从字符串中提取。源码 prompt.py 显示其处理流程为:
- 当
templates键不存在时,回退读取template键; - 把整个列表当作选项交给
prompt_choice_for_config()渲染并交互; - 用户选中后,用正则
r'\((.+)\)'提取括号内的路径(即./project-1)。
因此旧格式对路径书写有硬性约束:路径必须用英文括号包裹,且括号内不能包含多余括号,否则正则提取会出错。
5.2 新旧格式共存时的优先级
从 prompt.py 的判断顺序看,templates键优先:只要context['cookiecutter']中存在templates且其值为真(非空 dict),就按新格式处理;只有templates缺失或为空时,才会回退到旧格式的template。仓库测试 test_cookiecutter_nested_templates.py 同时覆盖了两种格式的夹具(fake-nested-templates与fake-nested-templates-old-style),可作为迁移时的对照。
6. 源码原理:一次"选择—递归"的调用链
嵌套配置文件机制的核心调度逻辑位于 main.py 的cookiecutter()主函数中:
if {"template", "templates"} & set(context["cookiecutter"].keys()): nested_template = choose_nested_template(context, repo_dir, no_input) return cookiecutter( template=nested_template, checkout=checkout, no_input=no_input, extra_context=extra_context, ... )其完整调用链如下:
- 识别嵌套入口:
generate_context()读入主cookiecutter.json后,主函数检测上下文中是否出现template或templates键,命中即判定为嵌套模板模式; - 弹出选择菜单:调用
choose_nested_template()(见 prompt.py),按前述新/旧格式逻辑取得用户选中的子模板路径; - 递归进入子模板:以选中路径作为新的
template参数递归调用cookiecutter(),此时子模板自身的cookiecutter.json会被正常读取,继续常规的变量提问与项目生成流程(generate_files); no_input全程透传:递归调用原样传递no_input等参数,保证两种模式行为一致。
值得注意的是,主函数通过{"template", "templates"} & set(...)这一集合交集判断,同时兼容新旧两种写法;而choose_nested_template()内部又做了一次templates→template的优先级判断,两者配合构成了完整的分发逻辑。
7. 测试验证与实操建议
7.1 测试用例解读
test_cookiecutter_nested_templates.py 通过参数化测试同时验证了两种格式:
@pytest.mark.parametrize( "template_dir,output_dir", [ ["fake-nested-templates", "fake-project"], ["fake-nested-templates-old-style", "fake-package"], ], ) def test_cookiecutter_nested_templates(...): mock_generate_files = mocker.patch("cookiecutter.main.generate_files") main_dir = (Path("tests") / template_dir).resolve() main.cookiecutter(f"{main_dir}", no_input=True) expected = (Path(main_dir) / output_dir).resolve() assert mock_generate_files.call_args[1]["repo_dir"] == f"{expected}"测试要点:以no_input=True调用后,断言最终生成阶段收到的repo_dir是主目录下的第一个子模板(新格式取fake-project,旧格式取fake-package),从侧面印证了第 4.2 节"--no-input取首个选项"的行为。
7.2 路径合法性检查
choose_nested_template()末尾对选中路径做了合法性校验(prompt.py):
- 路径必须非绝对路径(
not template.is_absolute()),即子模板必须声明为相对主目录的相对路径; - 不满足条件时抛出
ValueError: Illegal template path。
因此配置中请务必使用./project-1这类相对写法,不要写/abs/path。
7.3 实操建议汇总
- 优先使用新格式
templates:字段结构化、可读性好,且title/description缺省回退机制让最小配置只需一行"path"; - 子模板保持自包含:每个子目录都应具备完整的
cookiecutter.json与模板文件,可独立运行; - 注意
--no-input的局限:无交互模式下固定选择第一个子模板,若需改变默认目标,调整templates中键的排列顺序即可; - 可与其他高级特性叠加:子模板内部仍可使用 replay 回放、human_readable_prompts 友好提示等既有能力;若嵌套层级过深,也可参照 directories 用
directory参数指定仓库内子目录模板。
8. 版本演进小结
| 版本 | 变更内容 |
|---|---|
| 2.2.0 | 引入旧格式template键(字符串列表 + 括号路径) |
| 2.5.0 | 引入新格式templates键(结构化 dict,含path/title/description),并保持旧格式向后兼容 |
两种格式当前版本均可使用,迁移到新格式仅需把template数组改写为templates对象即可,交互体验完全一致。
【免费下载链接】cookiecutterA cross-platform command-line utility that creates projects from cookiecutters (project templates), e.g. Python package projects, C projects.项目地址: https://gitcode.com/gh_mirrors/co/cookiecutter
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考