Merge pull request #463 from theunkn0wn1/feature/grpcio_tools_protoc
use grpcio-tools protoc instead of system protoc
diff --git a/extra/poetry/pyproject.toml b/extra/poetry/pyproject.toml
index cc1a5bd..5b33dfe 100644
--- a/extra/poetry/pyproject.toml
+++ b/extra/poetry/pyproject.toml
@@ -18,9 +18,13 @@
[tool.poetry.dependencies]
python = ">=2.7"
protobuf = ">=3.6"
+grpcio-tools = {version = ">=1.26.0rc1", allow-prereleases = true, optional=true}
[tool.poetry.dev-dependencies]
+[tool.poetry.extras]
+grpcio-tools = ["grpcio-tools"]
+
[build-system]
requires = ["poetry>=0.12"]
build-backend = "poetry.masonry.api"
diff --git a/generator/proto/__init__.py b/generator/proto/__init__.py
index 93e7b33..29153d4 100644
--- a/generator/proto/__init__.py
+++ b/generator/proto/__init__.py
@@ -1,9 +1,12 @@
'''This file automatically rebuilds the proto definitions for Python.'''
+from __future__ import absolute_import
-import os
import os.path
import sys
-import subprocess
+
+import pkg_resources
+
+from ._utils import has_grpcio_protoc, invoke_protoc
dirname = os.path.dirname(__file__)
protosrc = os.path.join(dirname, "nanopb.proto")
@@ -12,8 +15,22 @@
if os.path.isfile(protosrc):
src_date = os.path.getmtime(protosrc)
if not os.path.isfile(protodst) or os.path.getmtime(protodst) < src_date:
- cmd = ["protoc", "--python_out=.", "nanopb.proto"]
- status = subprocess.call(cmd, cwd = dirname)
- if status != 0:
- sys.stderr.write("Failed to build nanopb_pb2.py: " + ' '.join(cmd) + "\n")
+ cmd = [
+ "protoc",
+ "--python_out={}".format(dirname),
+ protosrc,
+ "-I={}".format(dirname),
+ ]
+
+ if has_grpcio_protoc():
+ # grpcio-tools has an extra CLI argument
+ # from grpc.tools.protoc __main__ invocation.
+ _builtin_proto_include = pkg_resources.resource_filename('grpc_tools', '_proto')
+
+ cmd.append("-I={}".format(_builtin_proto_include))
+ try:
+ invoke_protoc(argv=cmd)
+ except:
+ sys.stderr.write("Failed to build nanopb_pb2.py: " + ' '.join(cmd) + "\n")
+ raise
diff --git a/generator/proto/_utils.py b/generator/proto/_utils.py
new file mode 100644
index 0000000..5cbc650
--- /dev/null
+++ b/generator/proto/_utils.py
@@ -0,0 +1,43 @@
+import subprocess
+
+
+def has_system_protoc():
+ # type: () -> bool
+ """ checks if a system-installed `protoc` executable exists """
+
+ try:
+ subprocess.check_call("protoc --version".split())
+ except FileNotFoundError:
+ return False
+ return True
+
+
+def has_grpcio_protoc():
+ # type: () -> bool
+ """ checks if grpcio-tools protoc is installed"""
+
+ try:
+ import grpc_tools.protoc
+ except ImportError:
+ return False
+ return True
+
+
+def invoke_protoc(argv):
+ # type: (list) -> typing.Any
+ """
+ Invoke protoc.
+
+ This routine will use grpcio-provided protoc if it exists,
+ using system-installed protoc as a fallback.
+
+ Args:
+ argv: protoc CLI invocation
+ """
+ if has_grpcio_protoc():
+ import grpc_tools.protoc as protoc
+ return protoc.main(argv)
+ if has_system_protoc():
+ return subprocess.check_call(argv)
+
+ raise FileNotFoundError("Neither grpc-tools nor system provided protoc could be found.")