多彩编程 多彩编程MZPH · CODE BLOG
ARTICLE DETAIL

文章详情

深耕前端与后端开发技术的一线实战笔记与踩坑复盘。

Jinja2 模板引擎实战:mypy_boto3_builder 如何一套管线输出多种类型包

Jinja2 模板引擎实战:mypy_boto3_builder 如何一套管线输出多种类型包 Jinja2 模板引擎实战mypy_boto3_builder 如何一套管线输出多种类型包【免费下载链接】mypy_boto3_builderType annotations builder for boto3 compatible with VSCode, PyCharm, Emacs, Sublime Text, pyright and mypy.项目地址: https://gitcode.com/gh_mirrors/my/mypy_boto3_builder如果你写过 AWS 相关的 Python 代码一定遇到过这样的烦恼boto3.client(s3)返回的对象在 IDE 里完全失明没有自动补全、没有参数提示、没有类型检查。这背后的原因是 boto3 在运行时动态生成客户端。而 mypy_boto3_builder 正是为解决这个问题而生的类型注解生成器——它基于Jinja2 模板引擎用一套渲染管线为 boto3、aioboto3、aiobotocore 生态同时产出types-boto3、types-aioboto3、types-aiobotocore等几十种类型包。今天我们就拆开它的模板工厂看看这套管线是如何做到一份数据、多端输出的。为什么 boto3 生态需要类型包boto3 的客户端和资源对象都是运行时通过 Service Model 动态构建的静态类型检查器mypy、pyright在编译期根本无法知道create_bucket有哪些参数、返回什么结构。于是社区的做法是离线生成类型存根type stubs再以独立 PyPI 包的形式发布让 IDE 和类型检查工具直接读取这些定义。安装类型包之后你的编辑器会立刻获得完整的代码补全、参数提示和错误检查效果就像下面这张动图展示的那样而这一切的幕后功臣就是项目的 templates/ 目录下那几百个 Jinja2 模板文件。一套模板引擎如何服务四种产品线走进 templates/ 目录你会发现模板并不是一锅乱炖而是按照产品线精细分层的common/—— 所有产品共享的公共零件类模板、函数模板、TypedDict 模板、README、LICENSE 等types-boto3/—— 同步版 boto3 类型包types-aioboto3/、types-aiobotocore/—— 异步版类型包types-boto3-service/、types-aiobotocore-service/—— 按单个 AWS 服务拆分的独立小包mypy-boto3/—— 早期的 boto3-stubs 包boto34/—— 面向 boto3 4.x 的新一代模板每个产品线目录下结构高度一致client.pyi.jinja2、literals.pyi.jinja2、type_defs.pyi.jinja2、waiter.pyi.jinja2……这意味着换一套模板就能换一种产品而数据模型完全不用变。核心机制一JinjaManager 统一掌管模板加载 ️整个渲染入口集中在mypy_boto3_builder/jinja_manager.py的JinjaManager类中它只做了三件小事却非常关键FileSystemLoader 定位模板所有模板以TEMPLATES_PATH即mypy_boto3_builder/templates/为根目录加载模板之间可以用{% include %}互相引用比如服务端点的client.pyi.jinja2就复用了common/class.py.jinja2StrictUndefined 严格模式模板里引用任何未定义的变量都会直接抛错绝不静默输出空串——这让生成的代码悄悄出错变得不可能模板缓存同一模板只解析一次后续直接命中缓存在需要批量生成 400 AWS 服务的场景下省下了大量重复解析开销。此外它还向模板环境注入了两个自定义过滤器escape_md转义 Markdown 特殊字符和format_python对渲染结果做代码格式化让渲染即成品成为可能。核心机制二一份数据模型同时渲染 .py 与 .pyi Jinja2 只管怎么渲染渲染什么则由mypy_boto3_builder/structures/中的数据模型决定。ServicePackage、Client、Method、TypeAnnotation这些结构体把解析好的 AWS 服务定义组织成模板可用的 Python 对象。真正精妙的设计在mypy_boto3_builder/writers/package_writer.py里。看_get_service_package_template_paths方法你会发现同一个模板可以同时输出多个文件。例如client.pyi.jinja2会同时渲染为client.pyi存根文件和client.py运行时模块version.py.jinja2同理。通过TemplateRender把一个模板 多个输出路径绑定在一起写一份模板就自动覆盖了 stubs 场景和运行时场景从源头杜绝了两处代码不一致的问题。核心机制三生成器策略决定给谁穿哪件衣服 模板是衣服数据是人而给谁穿什么由mypy_boto3_builder/generators/下的生成器负责。它们都继承自base_generator.py中的抽象类BaseGeneratorTypesBoto3Generator—— 产出同步 types-boto3 包AioBoto3Generator、AioBotocoreGenerator—— 产出异步类型包MypyBoto3Generator、Boto34Generator—— 其他产品线的实现每个生成器只需指定自己的service_template_path指向对应产品线的模板目录并实现_get_postprocessor返回相应的后处理器。解析、渲染、打包的骨架流程全部复用基类新增一种产品线的成本被压到极低。一套管线完整跑一遍从 Service Model 到 PyPI 包 把上面的机制串起来一条完整的生产线是这样运转的解析parsers/service_package_parser.py读取 botocore 的 Service Model得到结构化的ServicePackage后处理postprocessors/按产品线做差异化修正——比如aio_imports.py把同步 import 改写为异步模块aiobotocore.py、aioboto3.py分别处理各自的 API 差异渲染PackageWriter把数据模型喂给JinjaManager渲染出的模板.py/.pyi/Markdown 文档一次成型格式化渲染结果立刻交给 ruff 统一格式化见writers/ruff_formatter.py保证 400 多个服务生成的代码风格完全一致打包utils/package_builder.py构建 wheel/sdist甚至自动处理版本冲突、上传 PyPI。你只需在命令行指定产品与服务的组合生成器就会自动挑选对应模板跑完整个管线真正做到一套管线、多种输出。小结从 mypy_boto3_builder 学到的模板架构心法 回头看这个项目Jinja2 模板引擎的使用思路非常值得借鉴模板按产品线分层公共部分下沉到common/避免重复维护严格模式 缓存渲染又快又不会悄悄出错一个模板多输出.py 与 .pyi从机制上保证文件一致性生成器策略模式决定用哪套模板新增产品只需加目录 加生成器。如果你也在做代码生成工具或者正为多端输出同一份数据头疼不妨 clone 下 mypy_boto3_builder 这个项目重点读一读mypy_boto3_builder/templates/和jinja_manager.py相信这套模板工厂的实战设计会给你不少启发。【免费下载链接】mypy_boto3_builderType annotations builder for boto3 compatible with VSCode, PyCharm, Emacs, Sublime Text, pyright and mypy.项目地址: https://gitcode.com/gh_mirrors/my/mypy_boto3_builder创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表