Protobuf生成的Python包隐式相对导入导致外部导入失败问题
Python gRPC打包后导入模块不存在问题解决方案
问题根因
protoc默认生成的Python gRPC代码使用不带根包前缀的绝对导入规则:
- 本地开发时,你在
alphausblue目录下执行代码,当前工作目录会被自动加入Python的模块搜索路径sys.path,api作为当前目录下的子目录可以被直接识别为模块,导入正常 - 打包安装后,所有代码都位于site-packages下的
alphausblue目录内,api是alphausblue的子包而非顶层模块,直接搜索api模块自然找不到
可行解决方案
方案1:生成代码时指定根包前缀(最推荐)
在执行protoc生成代码的命令时,将setup.py所在的项目根目录设置为proto搜索路径,同时保证proto文件中的package声明与你的Python包结构对齐:
# 在项目根目录(setup.py同级)执行以下命令 protoc --proto_path=./ \ --python_out=./ \ --grpc_python_out=./ \ alphausblue/api/ripple/org.proto
生成的代码会自动带上alphausblue前缀,导入语句会变为from alphausblue.api.ripple import org_pb2 as api_dot_ripple_dot_org__pb2,安装后导入完全正常。
方案2:批量修改生成文件的导入语句
如果不想调整protoc生成逻辑,可以写个简单的脚本批量替换所有生成的_pb2.py和_pb2_grpc.py文件中的导入前缀:
# 放在项目根目录执行即可 import os import fileinput root_dir = "./alphausblue" for root, dirs, files in os.walk(root_dir): for f in files: if f.endswith("_pb2.py") or f.endswith("_pb2_grpc.py"): f_path = os.path.join(root, f) with fileinput.FileInput(f_path, inplace=True) as fd: for line in fd: print(line.replace("from api.ripple", "from alphausblue.api.ripple"), end='')
方案3:模块映射临时兼容(不推荐用于生产环境)
如果不想修改生成的代码,可以在alphausblue/__init__.py中添加模块映射代码,将顶层api模块指向自身的子包:
import sys from . import api sys.modules["api"] = api
该方案不需要修改其他代码,但是属于模块路径劫持的 hack 方案,可能会和其他同名顶层包冲突。
内容的提问来源于stack exchange,提问作者Woody1193
相关产品推荐
相关产品推荐

