
开发工具CLI代码生成【免费下载链接】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 仓库中tests/undefined-variable/file-content/{{cookiecutter.project_slug}}/README.rst这一测试夹具为切入点深入剖析 Cookiecutter 在渲染模板时如何处理未定义变量undefined variable。读者将理解 Cookiecutter 为什么会在生成项目时“宁可报错也不输出空白”掌握UndefinedVariableInTemplate异常从抛出、包装到 CLI 展示的完整链路并学会在实际模板开发中如何避免和排查这类问题。一、从一份测试夹具看未定义变量的典型场景在 Cookiecutter 仓库的 tests/undefined-variable/ 目录下存放着一组专门用于验证“模板引用了上下文中不存在的变量”时行为的测试夹具。它分为三个子场景覆盖了未定义变量可能出现的全部位置夹具目录未定义变量的位置触发文件file-content/文件内容中{{cookiecutter.project_slug}}/README.rstfile-name/文件名中{{cookiecutter.foobar}}一个文件名dir-name/目录名中{{cookiecutter.foobar}}/helloworld.py本次聚焦的关联文档正是file-content场景中的模板文件 tests/undefined-variable/file-content/{{cookiecutter.project_slug}}/README.rst其完整内容如下{{cookiecutter.project_slug}} {% for _ in cookiecutter.project_slug %}{% endfor %} {{cookiecutter.foobar}} https://github.com/{{cookiecutter.github_username}}/{{cookiecutter.project_slug}}这是一份典型的 reStructuredText 标题模板第一行用{{cookiecutter.project_slug}}输出项目名第二行用 Jinja2 的for循环按项目名长度生成等号下划线第四行引用{{cookiecutter.foobar}}末行拼接 GitHub 地址。其中project_slug与github_username都是预期存在的上下文变量而foobar是故意不定义的变量——它既不在上下文中也没有出现在 tests/undefined-variable/file-name/cookiecutter.json 之类的配置里。这个夹具的目的就是验证 Cookiecutter 遇到这种情况时能否正确报错而不是静默输出空白。二、严格渲染环境StrictUndefined 是“零容忍”的根源Cookiecutter 之所以会对未定义变量报错根源在于它构建 Jinja2 环境时启用了严格模式。在 cookiecutter/environment.py 中可以看到class StrictEnvironment(ExtensionLoaderMixin, Environment): Create strict Jinja2 environment. Jinja2 environment will raise error on undefined variable in template- rendering context. def __init__(self, **kwargs: Any) - None: ... super().__init__(undefinedStrictUndefined, **kwargs)关键点在于undefinedStrictUndefined。Jinja2 默认的Undefined对象在遇到未定义变量时只会返回一个“可打印为空串的占位对象”模板照常渲染、页面悄悄缺失内容而StrictUndefined则会在任何对未定义变量的访问读取、输出、参与运算时立即抛出jinja2.exceptions.UndefinedError。Cookiecutter 选择严格模式等于明确声明模板里出现未定义变量属于硬错误必须让用户看到不能靠输出空内容掩盖问题。同时StrictEnvironment通过ExtensionLoaderMixin加载了内置扩展JsonifyExtension、RandomStringExtension、SlugifyExtension、TimeExtension、UUIDExtension以及模板cookiecutter.json中_extensions键声明的自定义扩展这意味着未定义变量的检测同样适用于经过过滤器如slugify加工后的表达式。三、完整报错链路从 UndefinedError 到 UndefinedVariableInTemplate当模板渲染触发了UndefinedError后Cookiecutter 并不会把它原样抛出而是会在 cookiecutter/generate.py 的generate_files()中捕获并包装成语义更清晰的领域异常。该函数对三类渲染位置分别做了处理1. 项目目录名无法渲染对应dir-name场景的根目录try: project_dir, output_directory_created render_and_create_dir( unrendered_dir, context, output_dir, env, overwrite_if_exists ) except UndefinedError as err: msg fUnable to create project directory {unrendered_dir} raise UndefinedVariableInTemplate(msg, err, context) from err2. 嵌套目录名无法渲染对应dir-name场景的{{cookiecutter.foobar}}子目录try: render_and_create_dir(unrendered_dir, context, output_dir, env, overwrite_if_exists) except UndefinedError as err: if delete_project_on_failure: rmtree(project_dir) _dir os.path.relpath(unrendered_dir, output_dir) msg fUnable to create directory {_dir} raise UndefinedVariableInTemplate(msg, err, context) from err3. 文件名或文件内容无法渲染对应file-name与本文档所在的file-content场景try: generate_file(project_dir, infile, context, env, skip_if_file_exists) except UndefinedError as err: if delete_project_on_failure: rmtree(project_dir) msg fUnable to create file {infile} raise UndefinedVariableInTemplate(msg, err, context) from err包装后的异常类型定义在 cookiecutter/exceptions.py 中class UndefinedVariableInTemplate(CookiecutterException): Exception for out-of-scope variables. ... def __init__(self, message, error, context) - None: self.message message self.error error self.context context def __str__(self) - str: return ( f{self.message}. fError message: {self.error.message}. fContext: {self.context} )它同时携带三层信息用户可读的错误描述message、底层 Jinja2 报错详情error包含具体未定义变量名以及渲染时使用的完整上下文context即变量字典。这种设计让异常既可以用于编程式捕获也便于 CLI 层做格式化输出。值得注意的还有delete_project_on_failure与keep_project_on_failure两个开关默认情况下如果generate_files()中途失败已经创建的项目目录会被回滚清理rmtree(project_dir)保证不会在磁盘上留下残缺的半成品项目只有显式传入keep_project_on_failureTrue时才保留已生成的内容这在测试 tests/test_generate_files.py 的test_keep_project_on_failure用例中有直接验证。四、CLI 层的友好输出用户实际看到的报错异常最终由 cookiecutter/cli.py 的main()入口捕获并格式化输出except UndefinedVariableInTemplate as undefined_err: click.echo(f{undefined_err.message}) click.echo(fError message: {undefined_err.error.message}) context_str json.dumps(undefined_err.context, indent4, sort_keysTrue) click.echo(fContext: {context_str}) sys.exit(1)用户在命令行执行cookiecutter生成项目时如果模板引用了未定义变量将看到类似下面的三段式错误并以退出码 1 结束Unable to create file README.rst Error message: collections.OrderedDict object has no attribute foobar Context: { cookiecutter: { github_username: hackebrot, project_slug: testproject } }第一行指明失败发生在哪个文件第二行给出 Jinja2 层面的具体缺失属性foobar第三行以 JSON 形式完整打印当时的上下文方便开发者对照排查是模板写错了变量名还是上下文确实缺少该变量。测试 tests/test_cli.py 中同样断言了这种错误文本格式。五、测试如何验证这一行为tests/test_generate_files.py 中针对三个夹具场景各有一个核心测试公共夹具undefined_context只提供两个变量pytest.fixture def undefined_context(): return { cookiecutter: {project_slug: testproject, github_username: hackebrot} }其中test_raise_undefined_variable_file_content直接作用于本文档所在的file-content场景def test_raise_undefined_variable_file_content(output_dir, undefined_context) - None: Verify correct error raised when file content cannot be rendered. with pytest.raises(exceptions.UndefinedVariableInTemplate) as err: generate.generate_files( repo_dirtests/undefined-variable/file-content/, output_diroutput_dir, contextundefined_context, ) error err.value assert error.message Unable to create file README.rst assert error.context undefined_context assert not Path(output_dir).joinpath(testproject).exists()该测试验证了两个关键行为报错信息指向具体文件README.rst且失败后输出目录中不存在半成品项目assert not ...exists()证实了前述的失败回滚逻辑。test_raise_undefined_variable_file_name、test_raise_undefined_variable_dir_name则分别验证文件名与目录名场景其中目录场景的错误信息形如Unable to create directory testproject/{{cookiecutter.foobar}}。若想亲手复现可在仓库根目录运行pytest tests/test_generate_files.py。六、实战建议如何避免与排查未定义变量结合上述机制在开发 Cookiecutter 模板cookiecutter时可以总结出以下实践准则所有模板中引用的变量都应在cookiecutter.json中给出默认值。Jinja2 渲染时上下文完全来自cookiecutter.json定义的键与用户输入任何未声明键都会触发StrictUndefined报错。这也是file-content夹具中foobar必然失败的根本原因——它从未被任何配置声明。善用私有变量前缀_与默认上下文注入。例如_copy_without_render、_extensions、_new_lines等键本身就是在上下文里传递的配置项它们由 Cookiecutter 内部使用不会出现在交互提示中。排查时优先看Error message行它直接给出缺失的变量名再对照Context的 JSON 输出核对变量是否拼写错误或大小写不一致。在自动化调用中主动捕获UndefinedVariableInTemplate如通过 docs/advanced/calling_from_python.rst 介绍的generate_files()编程接口利用其message、error、context三属性生成结构化错误日志便于 CI 流程定位。如果模板确实需要“可选变量”应通过 Jinja2 的条件判断如{% if cookiecutter.foobar is defined %}...{% endif %}显式保护而不是依赖默认的宽松渲染——因为 Cookiecutter 的StrictEnvironment默认并不提供这种宽容。七、总结从一份仅有六行的测试夹具模板出发我们完整还原了 Cookiecutter 对模板未定义变量的处理哲学通过StrictEnvironment的StrictUndefined把问题暴露在渲染阶段由 generate.py 捕获UndefinedError并包装为携带上下文信息的UndefinedVariableInTemplate最终由 cli.py 以三段式错误呈现给用户同时配合失败回滚避免留下残缺项目。这套机制保证了 Cookiecutter 生成的每一个项目都来自完整、可验证的模板渲染结果而不是带着静默缺漏的半成品——这正是它的模板在工程实践中值得信赖的原因之一。赞分享开发工具CLI代码生成【免费下载链接】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点击查看免费下载相关推荐TRL 奖励模型卡片模板 rm_model_card.md 深度解析模板结构、变量渲染链路与检查点联动机制TRL 奖励模型卡片模板 rm_model_card.md 深度解析模板结构、变量渲染链路与检查点联动机制 本文以 rm_model_card.md http人工智能大模型强化学习RLHF预训练微调LoRA{{ cookiecutter.project_name }}{{ cookiecutter.project_name }} {{ cookiecutter.description }} Author {{ cookiec开发工具CLI代码生成Cookiecutter 模板故障排查完全指南Jinja 转义、_copy_without_render 与常见错误处理Cookiecutter 模板故障排查完全指南Jinja 转义、 _copy_without_render 与常见错误处理 本文是 Cookiecutter开发工具CLI代码生成上一篇Gravitino多引擎集成实战Trino、Spark与Flink统一元数据访问方案下一篇解决FanControl更新后控制器识别问题从V237到V238迁移完全指南创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考