【问题标题】:Python project using protocol buffers, Deployment issues使用协议缓冲区的 Python 项目,部署问题
【发布时间】:2015-03-06 18:22:41
【问题描述】:

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

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

在这里寻找可行的最佳实践解决方案。可以假设目标平台已经安装了protoc-compiler。

编辑:

由于人们询问失败的原因。在 Mac 上,protobuf 版本是 2.6.1。在 Pi 上是 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

【问题讨论】:

  • 我有类似的问题,总是在 PI 上编译。问题源于不同的 protobuf 版本。那么 - 你在各自的机器上有哪些版本,你不能将它们升级到相同的版本吗?
  • 我真的很想避免这种情况。我可以(可能)在我控制的机器上做到这一点,但感觉不是正确的方法。将软件分发给其他人时,它可能会失败。当他们的当前版本可以(通过重新编译)使用时让他们安装特定版本的协议缓冲区,感觉不对。
  • 您是否研究过 - 导致导入错误的原因?我会选择打包_pb2.py文件策略和研究导入错误问题。
  • 满足用户的最低要求。因此,您要么要求他们安装某些软件,要么将其捆绑在您的应用程序中。您可以选择某个版本的协议缓冲区并将整个堆栈包含在您的产品中。只需确保在导入时先将其拾取即可。
  • @deets:我尊重这种观点,它肯定会让我的生活更轻松。但是,如果我可以从设置脚本中调用 protoc,那么特定的 protobuf 版本并不是真正的要求。并且将来当内部 API 再次更改并且突然用户需要运行旧版本时,将特定的固定版本作为要求可能会更加成问题。我真的很想避免那种支持地狱。所以让我们假设分发 _pb2.py 文件不是一个实际的选择。

标签: python installation protocol-buffers setuptools software-distribution


【解决方案1】:

好的,我解决了这个问题,不需要用户安装特定的旧版本或在我的开发机器以外的另一个平台上编译 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 类以添加构建和清理协议缓冲区。

# 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 }   

需要添加到 setup 调用中,并且一切正常。仍然需要检查可能的怪癖,但到目前为止,它在 Mac 和 Pi 上都能完美运行。

【讨论】:

    【解决方案2】:

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

    【讨论】:

      【解决方案3】:

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

      查看:https://pypi.python.org/pypi/protobuf-setuptools

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-09-18
        • 1970-01-01
        • 1970-01-01
        • 2021-12-19
        相关资源
        最近更新 更多