"""
Converts a given ASCII proto into a binary resource.
"""
from __future__ import print_function
import abc
from importlib import util as imp_util
import optparse
import os
import re
import subprocess
import sys
import traceback
class GoogleProtobufModuleImporter:
"""A custom module importer for importing google.protobuf.
See PEP #302 (https://www.python.org/dev/peps/pep-0302/) for full information
on the Importer Protocol.
"""
def __init__(self, paths):
"""Creates a loader that searches |paths| for google.protobuf modules."""
self._paths = paths
def _fullname_to_filepath(self, fullname):
"""Converts a full module name to a corresponding path to a .py file.
e.g. google.protobuf.text_format -> pyproto/google/protobuf/text_format.py
"""
for path in self._paths:
filepath = os.path.join(path, fullname.replace('.', os.sep) + '.py')
if os.path.isfile(filepath):
return filepath
return None
def _module_exists(self, fullname):
return self._fullname_to_filepath(fullname) is not None
def find_module(self, fullname, path=None):
"""Returns a loader module for the google.protobuf module in pyproto."""
if (fullname.startswith('google.protobuf.')
and self._module_exists(fullname)):
return self
return None
def load_module(self, fullname):
"""Loads the module specified by |fullname| and returns the module."""
if fullname in sys.modules:
return sys.modules[fullname]
if (not fullname.startswith('google.protobuf.') or
not self._module_exists(fullname)):
raise ImportError(fullname)
filepath = self._fullname_to_filepath(fullname)
spec = imp_util.spec_from_file_location(fullname, filepath)
loaded = imp_util.module_from_spec(spec)
spec.loader.exec_module(loaded)
return loaded
class BinaryProtoGenerator:
def _IsInVirtualEnv(self):
return hasattr(sys, 'real_prefix')
def _ImportProtoModules(self, paths):
"""Import the protobuf modules we need. |paths| is list of import paths"""
for path in paths:
sys.path.insert(1, path)
if self._IsInVirtualEnv():
sys.meta_path.append(GoogleProtobufModuleImporter(paths))
import google.protobuf.text_format as text_format
globals()['text_format'] = text_format
self.ImportProtoModule()
def _GenerateBinaryProtos(self, opts):
""" Read the ASCII proto and generate one or more binary protos. """
with open(opts.infile, 'r') as ifile:
ascii_pb_str = ifile.read()
full_pb = self.EmptyProtoInstance()
text_format.Merge(ascii_pb_str, full_pb)
self.ValidatePb(opts, full_pb);
self.ProcessPb(opts, full_pb)
@abc.abstractmethod
def ImportProtoModule(self):
""" Import the proto module to be used by the generator. """
pass
@abc.abstractmethod
def EmptyProtoInstance(self):
""" Returns an empty proto instance to be filled by the generator."""
pass
@abc.abstractmethod
def ValidatePb(self, opts, pb):
""" Validate the basic values of the protobuf. The
file_type_policies_unittest.cc will also validate it by platform,
but this will catch errors earlier.
"""
pass
@abc.abstractmethod
def ProcessPb(self, opts, pb):
""" Process the parsed prototobuf. """
pass
def AddCommandLineOptions(self, parser):
""" Allows subclasses to add any options the command line parser. """
pass
def AddExtraCommandLineArgsForVirtualEnvRun(self, opts, command):
""" Allows subclasses to add any extra command line arguments when running
this under a virtualenv."""
pass
def VerifyArgs(self, opts):
""" Allows subclasses to check command line parameters before running. """
return True
def Run(self):
parser = optparse.OptionParser()
parser.add_option('-w', '--wrap', action="store_true", default=False,
help='Wrap this script in another python '
'execution to disable site-packages. This is a '
'fix for http://crbug.com/605592')
parser.add_option('-i', '--infile',
help='The ASCII proto file to read.')
parser.add_option('-d', '--outdir',
help='Directory underwhich binary file(s) will be ' +
'written')
parser.add_option('-o', '--outbasename',
help='Basename of the binary file to write to.')
parser.add_option('-p', '--path', action="append",
help='Repeat this as needed. Directory(s) containing ' +
'the your_proto_definition_pb2.py and ' +
'google.protobuf.text_format modules')
self.AddCommandLineOptions(parser)
(opts, args) = parser.parse_args()
if opts.infile is None or opts.outdir is None or opts.outbasename is None:
parser.print_help()
return 1
if opts.wrap and not self._IsInVirtualEnv():
command = [sys.executable, '-S', '-s', sys.argv[0]]
command += ['-i', opts.infile]
command += ['-d', opts.outdir]
command += ['-o', opts.outbasename]
for path in opts.path:
command += ['-p', path]
self.AddExtraCommandLineArgsForVirtualEnvRun(opts, command);
sys.exit(subprocess.call(command))
self._ImportProtoModules(opts.path)
if not self.VerifyArgs(opts):
print("Wrong arguments")
return 1
try:
self._GenerateBinaryProtos(opts)
except Exception as e:
print("ERROR: Failed to render binary version of %s:\n %s\n%s" %
(opts.infile, str(e), traceback.format_exc()))
return 1