使用Buf CLI生成Python代码时,如何实现含点号的嵌套文件夹输出结构?
我完全理解你的需求——你希望用Buf生成Python代码时,直接输出components/com.company.app1/entity1/v1/这类带点号的目录结构,而不是默认的层级拆分目录components/com/company/app1/entity1/v1/,同时还要避免手动维护路径或做后处理。结合你给出的配置和限制(远程Python插件不支持opt参数、Linter不允许目录名带点),我整理了几个可行的方案:
方案1:针对单个/少量App,直接指定插件输出路径
如果你的App数量不多(比如只有app1和app2),可以在buf.gen.yaml中为每个顶层包单独配置插件,指定对应的输出目录:
version: v2 managed: enabled: true plugins: # 处理app1的proto文件 - remote: buf.build/protocolbuffers/python out: components/com.company.app1 paths: - "com/company/app1/**" - remote: buf.build/protocolbuffers/pyi out: components/com.company.app1 paths: - "com/company/app1/**" # 处理app2的proto文件 - remote: buf.build/protocolbuffers/python out: components/com.company.app2 paths: - "com/company/app2/**" - remote: buf.build/protocolbuffers/pyi out: components/com.company.app2 paths: - "com/company/app2/**"
这个方案的优点是配置简单直接,不需要额外工具;缺点是新增App时需要手动添加插件配置,不符合你“无需维护路径”的理想需求,但对于App数量固定的场景很实用。
方案2:用脚本动态生成Buf生成配置(适合多App/自动场景)
因为你是在GitHub Actions或Taskfile中执行生成操作,可以先通过脚本自动提取所有顶层App包名,动态生成buf.gen.yaml,完全无需手动维护路径:
- 先写一个Shell脚本(也可以用Python实现),自动识别所有App包并生成配置:
#!/bin/bash # 提取所有proto文件的包名,截取前3段(com.company.appX)并去重 APP_PACKAGES=$(buf ls files --format='{{.Package}}' | cut -d '.' -f 1-3 | sort -u) # 生成buf.gen.yaml的基础内容 cat > buf.gen.yaml << EOF version: v2 managed: enabled: true plugins: EOF # 为每个App自动添加插件配置 for pkg in $APP_PACKAGES; do # 把包名的点替换为斜杠,用于匹配proto文件路径 pkg_path=$(echo $pkg | tr '.' '/') cat >> buf.gen.yaml << EOF - remote: buf.build/protocolbuffers/python out: components/$pkg paths: - "$pkg_path/**" - remote: buf.build/protocolbuffers/pyi out: components/$pkg paths: - "$pkg_path/**" EOF done
- 在你的GitHub Actions或Taskfile中,先运行这个脚本生成
buf.gen.yaml,再执行buf generate命令即可。
这个方案完全满足你“无需维护路径”的需求,新增App时脚本会自动识别并添加配置,适合动态场景。
方案3:调整Python包结构规范(推荐,长期更稳定)
从Python的包规范角度来说,com/company/app1这种层级目录才是标准结构,com.company.app1作为单个目录名反而不符合Python的模块导入逻辑(会导致import com.company.app1无法正常找到模块,除非手动配置命名空间包)。
如果可以接受调整需求,你可以保留默认生成的层级目录,然后通过添加命名空间包的__init__.py文件,让Python可以正常导入:
- 在
components/com/__init__.py中添加:
__import__('pkgutil').extend_path(__path__, __name__)
- 在
components/com/company/__init__.py中添加同样的内容。
这样Python就会把com/company/app1识别为com.company.app1模块,既符合规范,又不需要修改Buf的生成配置,长期维护更稳定。
另外,关于你提到的Linter不允许目录名带点的问题,你现有proto/com/company/app1/...的目录结构是完全符合规范的——Linter的要求是包名和目录结构一一对应,这个结构和包名com.company.app1.entity1.v1完全匹配,无需修改。
备注:内容来源于stack exchange,提问作者Kim Rasmussen

