Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/nanopb/nanopb.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'generator/proto/__init__.py')
-rw-r--r--generator/proto/__init__.py124
1 files changed, 101 insertions, 23 deletions
diff --git a/generator/proto/__init__.py b/generator/proto/__init__.py
index e2e8000..cfab910 100644
--- a/generator/proto/__init__.py
+++ b/generator/proto/__init__.py
@@ -1,34 +1,37 @@
-'''This file automatically rebuilds the proto definitions for Python.'''
+'''This file dynamically builds the proto definitions for Python.'''
from __future__ import absolute_import
+import os
import os.path
import sys
-
+import tempfile
+import shutil
+import traceback
import pkg_resources
-
from ._utils import has_grpcio_protoc, invoke_protoc, print_versions
-dirname = os.path.dirname(__file__)
-protosrc = os.path.join(dirname, "nanopb.proto")
-protodst = os.path.join(dirname, "nanopb_pb2.py")
-rebuild = False
+# Compatibility layer to make TemporaryDirectory() available on Python 2.
+try:
+ from tempfile import TemporaryDirectory
+except ImportError:
+ class TemporaryDirectory:
+ '''TemporaryDirectory fallback for Python 2'''
+ def __init__(self, prefix = 'tmp', dir = None):
+ self.prefix = prefix
+ self.dir = dir
-if os.path.isfile(protosrc):
- src_date = os.path.getmtime(protosrc)
- if not os.path.isfile(protodst) or os.path.getmtime(protodst) < src_date:
- rebuild = True
+ def __enter__(self):
+ self.dir = tempfile.mkdtemp(prefix = self.prefix, dir = self.dir)
+ return self.dir
-if not rebuild:
- try:
- from . import nanopb_pb2
- except AttributeError as e:
- rebuild = True
- sys.stderr.write("Failed to import nanopb_pb2.py: " + str(e) + "\n"
- "Will automatically attempt to rebuild this.\n"
- "Verify that python-protobuf and protoc versions match.\n")
- print_versions()
+ def __exit__(self, *args):
+ shutil.rmtree(self.dir)
+
+def build_nanopb_proto(protosrc, dirname):
+ '''Try to build a .proto file for python-protobuf.
+ Returns True if successful.
+ '''
-if rebuild:
cmd = [
"protoc",
"--python_out={}".format(dirname),
@@ -40,11 +43,86 @@ if rebuild:
# 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
+ sys.stderr.write(traceback.format_exc() + "\n")
+ return False
+
+ return True
+
+def load_nanopb_pb2():
+ # To work, the generator needs python-protobuf built version of nanopb.proto.
+ # There are three methods to provide this:
+ #
+ # 1) Load a previously generated generator/proto/nanopb_pb2.py
+ # 2) Use protoc to build it and store it permanently generator/proto/nanopb_pb2.py
+ # 3) Use protoc to build it, but store only temporarily in system-wide temp folder
+ #
+ # By default these are tried in numeric order.
+ # If NANOPB_PB2_TEMP_DIR environment variable is defined, the 2) is skipped.
+ # If the value of the $NANOPB_PB2_TEMP_DIR exists as a directory, it is used instead
+ # of system temp folder.
+
+ build_error = None
+ proto_ok = False
+ tmpdir = os.getenv("NANOPB_PB2_TEMP_DIR")
+ temporary_only = (tmpdir is not None)
+ dirname = os.path.dirname(__file__)
+ protosrc = os.path.join(dirname, "nanopb.proto")
+ protodst = os.path.join(dirname, "nanopb_pb2.py")
+ proto_ok = False
+
+ if tmpdir is not None and not os.path.isdir(tmpdir):
+ tmpdir = None # Use system-wide temp dir
+
+ if os.path.isfile(protosrc):
+ src_date = os.path.getmtime(protosrc)
+ if not os.path.isfile(protodst) or os.path.getmtime(protodst) < src_date:
+ # Outdated, rebuild
+ proto_ok = False
+ else:
+ try:
+ from . import nanopb_pb2 as nanopb_pb2_mod
+ proto_ok = True
+ except Exception as e:
+ sys.stderr.write("Failed to import nanopb_pb2.py: " + str(e) + "\n"
+ "Will automatically attempt to rebuild this.\n"
+ "Verify that python-protobuf and protoc versions match.\n")
+ print_versions()
+
+ # Try to rebuild into generator/proto directory
+ if not proto_ok and not temporary_only:
+ proto_ok = build_nanopb_proto(protosrc, dirname)
+
+ try:
+ from . import nanopb_pb2 as nanopb_pb2_mod
+ except:
+ sys.stderr.write("Failed to import generator/proto/nanopb_pb2.py:\n")
+ sys.stderr.write(traceback.format_exc() + "\n")
+
+ # Try to rebuild into temporary directory
+ if not proto_ok:
+ with TemporaryDirectory(prefix = 'nanopb-', dir = tmpdir) as protodir:
+ proto_ok = build_nanopb_proto(protosrc, protodir)
+
+ if protodir not in sys.path:
+ sys.path.insert(0, protodir)
+
+ try:
+ import nanopb_pb2 as nanopb_pb2_mod
+ except:
+ sys.stderr.write("Failed to import %s/nanopb_pb2.py:\n" % protodir)
+ sys.stderr.write(traceback.format_exc() + "\n")
+
+ # If everything fails
+ if not proto_ok:
+ sys.stderr.write("\n\nGenerating nanopb_pb2.py failed.\n")
+ sys.stderr.write("Make sure that a protoc generator is available and matches python-protobuf version.\n")
+ print_versions()
+ sys.exit(1)
+ return nanopb_pb2_mod