Python中无需调用subprocess即可使用Protobuf编译器的方法
进程内调用Protobuf编译器实现方法
你不需要启动子进程调用grpc_tools.protoc,grpc_tools本身就提供了进程内直接调用的编译入口:命令行执行python -m grpc_tools.protoc本质是运行该模块内置的main函数,你只需要按照命令行参数的顺序把参数拼成列表传入即可,全程在当前Python进程内执行,没有额外子进程开销。
核心调用说明
- 传入
protoc.main()的参数列表格式和命令行参数完全一致,第一个参数固定为'protoc'(对应命令行的程序名占位),后面依次拼接编译所需的-I引入路径、输出路径、目标proto文件参数即可 - 函数为同步执行,返回
0代表编译成功,非0代表出现编译错误,可以直接做错误判断,比Popen异步执行更可控 - 用列表传参不需要手动拼接命令字符串,能自动规避路径带空格、特殊字符的转义问题,也不存在shell注入风险
改造后的完整代码
import os import argparse from grpc_tools import protoc def dir_path(string): if os.path.isdir(string): return string else: raise NotADirectoryError(string) def compProtos(frompath, topath): for dirpath, dirs, files in os.walk(frompath): for filename in files: if filename.endswith('.proto'): proto_full_path = os.path.join(dirpath, filename) print(f'Currently generating file: {filename}, from path: {dirpath}') # 进程内直接调用编译逻辑,无需启动子进程 return_code = protoc.main([ 'protoc', f'-I{dirpath}', f'--python_out={topath}', f'--grpc_python_out={topath}', proto_full_path ]) if return_code != 0: raise RuntimeError(f"Failed to compile proto file: {proto_full_path}") if __name__ == "__main__": parser = argparse.ArgumentParser(description='Run gRPC on protobuffs') parser.add_argument('-from', dest="frompath", type=dir_path, required=True, help="Enter protobuffs path", metavar="FILE") parser.add_argument('-to', dest="topath", type=dir_path, default=".", help="Enter saving path", metavar="FILE") args = parser.parse_args() compProtos(args.frompath, args.topath) print("Done generating protobuffs, results in folder: " + args.topath)
和原Popen实现的差异
- 去掉了subprocess子进程启动逻辑,全程在当前进程执行,减少额外开销
- 修复了原代码中Popen未等待进程执行完成就继续遍历的问题,可同步获取编译结果
- 用
os.path.join拼接文件路径,兼容Windows/Linux跨平台路径格式 - 增加了编译结果状态码校验,编译失败会直接抛出异常,不会静默跳过错误
如果你使用的是原生protobuf编译器(不是grpc_tools打包的版本),也可以用类似逻辑导入
google.protobuf.compiler相关模块调用编译能力,但日常开发中grpc_tools打包的protoc已经覆盖了普通proto+gRPC proto的全量编译需求,直接调用上述接口即可满足使用。
内容的提问来源于stack exchange,提问作者Oac Roland
相关产品推荐
相关产品推荐

