使用协议缓冲区的Python项目,部署问题

2024-09-27 23:15:46 发布

您现在位置:Python中文网/ 问答频道 /正文

我有一个Python项目,它使用setuptools进行部署,我主要遵循关于项目结构的this guide。该项目使用Google协议缓冲区来定义网络消息格式。我的主要问题是如何在安装期间使setup.py调用protoc编译器,以将定义构建到一个_pb2.py文件中。

this question中,建议只将生成的pb2.py文件与项目一起分发。虽然这可能适用于非常相似的平台,但我发现有几个情况下这不起作用。例如,当我在一个使用Anaconda Python的Mac上进行开发,并将生成的pb2.py和项目的其余部分复制到一个运行Raspbian的Raspberry Pi时,总会有来自pb2.py模块的导入错误。但是,如果我在Pi上新编译.proto文件,项目将按预期工作。因此,分发编译后的文件似乎不是一个选项。

在这里寻找工作和最佳实践解决方案。可以假设protoc编译器安装在目标平台上。

编辑:

因为人们会问失败的原因。在Mac上,protobuf版本是2.6.1。圆周率是2.4.1。显然,生成的protoc编译器输出所使用的内部API已经改变。结果基本上是:

  File "[...]network_manager.py", line 8, in <module>
      import InstrumentControl.transports.serial_bridge_protocol_pb2 as protocol
  File "[...]serial_bridge_protocol_pb2.py", line 9, in <module>
      from google.protobuf import symbol_database as _symbol_database
  ImportError: cannot import name symbol_database

Tags: 文件项目pyimport编译器定义macpi
3条回答

好的,我解决了这个问题,不需要用户安装特定的旧版本,也不需要在我的开发机器之外的另一个平台上编译proto文件。它的灵感来自this setup.py script from protobuf itself

首先,需要找到protoc,可以使用

# Find the Protocol Compiler.
if 'PROTOC' in os.environ and os.path.exists(os.environ['PROTOC']):
  protoc = os.environ['PROTOC']
else:
  protoc = find_executable("protoc")

此函数将编译一个.proto文件并将_pb2.py放在同一位置。但是,行为可以任意改变。

def generate_proto(source):
  """Invokes the Protocol Compiler to generate a _pb2.py from the given
  .proto file.  Does nothing if the output already exists and is newer than
  the input."""

  output = source.replace(".proto", "_pb2.py")

  if (not os.path.exists(output) or
      (os.path.exists(source) and
       os.path.getmtime(source) > os.path.getmtime(output))):
    print "Generating %s..." % output

    if not os.path.exists(source):
      sys.stderr.write("Can't find required file: %s\n" % source)
      sys.exit(-1)

    if protoc == None:
      sys.stderr.write(
          "Protocol buffers compiler 'protoc' not installed or not found.\n"
          )
      sys.exit(-1)

    protoc_command = [ protoc, "-I.", "--python_out=.", source ]
    if subprocess.call(protoc_command) != 0:
      sys.exit(-1)

接下来,派生类build py和clean来添加building和cleaning协议缓冲区。

# List of all .proto files
proto_src = ['file1.proto', 'path/to/file2.proto']

class build_py(_build_py):
  def run(self):
    for f in proto_src:
        generate_proto(f)
    _build_py.run(self)

class clean(_clean):
  def run(self):
    # Delete generated files in the code tree.
    for (dirpath, dirnames, filenames) in os.walk("."):
      for filename in filenames:
        filepath = os.path.join(dirpath, filename)
        if filepath.endswith("_pb2.py"):
          os.remove(filepath)
    # _clean is an old-style class, so super() doesn't work.
    _clean.run(self)

最后,参数

cmdclass = { 'clean': clean, 'build_py': build_py }   

需要添加到对安装程序的调用中,一切都应该正常。仍然需要检查可能的怪癖,但到目前为止,它在Mac和Pi上工作得完美无瑕。

我刚刚开始使用protobuf setuptools包来使用这段代码中最理智的部分。它仍然需要改进,所以欢迎任何反馈!

检查一下:https://pypi.python.org/pypi/protobuf-setuptools

另一个解决方案是将protobuf库与应用程序捆绑在一起,而不是在目标计算机上使用已安装的版本。这样,您就知道没有版本与生成的代码不匹配。

相关问题 更多 >

    热门问题