完全指南:用 `templates` 键构建模板层级选择体系)
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-1description选择菜单中展示的补充说明缺省时与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_inputTrue运行时不再弹出菜单而是直接取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( templatenested_template, checkoutcheckout, no_inputno_input, extra_contextextra_context, ... )其完整调用链如下识别嵌套入口generate_context()读入主cookiecutter.json后主函数检测上下文中是否出现template或templates键命中即判定为嵌套模板模式弹出选择菜单调用choose_nested_template()见 prompt.py按前述新/旧格式逻辑取得用户选中的子模板路径递归进入子模板以选中路径作为新的template参数递归调用cookiecutter()此时子模板自身的cookiecutter.json会被正常读取继续常规的变量提问与项目生成流程generate_filesno_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_inputTrue) expected (Path(main_dir) / output_dir).resolve() assert mock_generate_files.call_args[1][repo_dir] f{expected}测试要点以no_inputTrue调用后断言最终生成阶段收到的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),仅供参考