protobuf生成的Python包内部的隐式相对导入使得从外部导入是不可能的



我有一个像这样的包:

setup.py
requirements.txt
alphausblue/
api/
ripple/
org_pb2.py
org/
v1/
org_pb2_grpc.py

在org_pb2_grpc.py中,我有以下导入行:

from api.ripple import org_pb2 as api_dot_ripple_dot_org__pb2

我遇到的问题是,当我从alphausblue目录内导入时,导入工作正确。但是,当我从包中创建车轮时,将其上传到Test PyPI,再次下载并尝试将其导入到测试环境中,如下所示:

>>> import alphausblue
>>> import alphausblue.org
>>> import alphausblue.org.v1
>>> import alphausblue.org.v1.org_pb2_grpc
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "anaconda3envstestlibsite-packagesalphausblueorgv1org_pb2_grpc.py", line 5, in <module>
from api.ripple import org_pb2 as api_dot_ripple_dot_org__pb2
ModuleNotFoundError: No module named 'api'

我得到上面的错误。我可以将代码从alphausblue/移动到/,然后导入工作,但我必须做org.v1.org_pb2_grpc,这不是我想要的。这似乎应该工作,所以我在这里错过了什么?

这似乎是protoc如何生成Python代码的问题。我将在这里调查可能的解决方案,看看我是否能自己解决这个问题。

所以,我尝试了一些事情,试图让Protobuf做我想要的,即…工作,但没能弄明白。因此,我对构建脚本做了如下修改:

mkdir -p generated/py/alphausblue
python3 -m grpc_tools.protoc -I . --python_out=./generated/py/alphausblue --grpc_python_out=./generated/py/alphausblue 
./org/v1/*.proto 
./kvstore/v1/*.proto 
./iam/v1/*.proto 
./admin/v1/*.proto 
./cost/v1/*.proto 
./billing/v1/*.proto 
./operations/v1/*.proto 
./preferences/v1/*.proto
python3 -m grpc_tools.protoc -I . --python_out=./generated/py/alphausblue 
$(for v in $(find ./api -type d); do echo -n "$v/*.proto "; done)
for package in $(find generated/py/alphausblue -mindepth 1 -maxdepth 1 -type d -printf "%f "); do
echo "Found package ${package}. Beginning replacement"
find generated/py/alphausblue/. -name '*.py' -exec sed -i -e "s/from ${package}/from alphausblue.${package}/g" {} ;
done

这里的前两个命令从.proto文件生成Python代码。下一个命令使用find和sed获取包本身中的所有顶级包名,并用绝对导入替换隐式的相对导入。

我想这会有帮助:

import os
import re
# Set the path to the directory containing the generated files
generated_files_dir = ""
# Set the relative import prefix
relative_import_prefix = "."
# Get a list of all generated files in the directory
generated_files = [file for file in os.listdir(generated_files_dir) if "pb2" in file]
# Define the regular expression pattern to match import statements
import_pattern = re.compile(r"^imports+(S+)s+ass+(S+)s*$", re.MULTILINE)
# Iterate over each generated file and modify the import statements
for generated_file in generated_files:
generated_file_path = os.path.join(generated_files_dir, generated_file)
# Read the contents of the generated file
with open(generated_file_path, "r") as f:
file_contents = f.read()
# Modify the import statements using relative imports
modified_contents = import_pattern.sub(r"from {} import 1 as 2".format(relative_import_prefix), file_contents)
# Write the modified contents back to the generated file
with open(generated_file_path, "w") as f:
f.write(modified_contents)

最新更新