| Index: tools/telemetry/third_party/gsutilz/third_party/protorpc/gen_protorpc.py
|
| diff --git a/tools/telemetry/third_party/gsutilz/third_party/protorpc/gen_protorpc.py b/tools/telemetry/third_party/gsutilz/third_party/protorpc/gen_protorpc.py
|
| new file mode 100755
|
| index 0000000000000000000000000000000000000000..101a70ff12f29bb6ecf5f0b6e0f8a7d5c5c04728
|
| --- /dev/null
|
| +++ b/tools/telemetry/third_party/gsutilz/third_party/protorpc/gen_protorpc.py
|
| @@ -0,0 +1,300 @@
|
| +#!/usr/bin/env python
|
| +#
|
| +# Copyright 2011 Google Inc.
|
| +#
|
| +# Licensed under the Apache License, Version 2.0 (the "License");
|
| +# you may not use this file except in compliance with the License.
|
| +# You may obtain a copy of the License at
|
| +#
|
| +# http://www.apache.org/licenses/LICENSE-2.0
|
| +#
|
| +# Unless required by applicable law or agreed to in writing, software
|
| +# distributed under the License is distributed on an "AS IS" BASIS,
|
| +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| +# See the License for the specific language governing permissions and
|
| +# limitations under the License.
|
| +#
|
| +
|
| +"""Command line tool for generating ProtoRPC definitions from descriptors."""
|
| +
|
| +import errno
|
| +import logging
|
| +import optparse
|
| +import os
|
| +import sys
|
| +
|
| +from protorpc import descriptor
|
| +from protorpc import generate_python
|
| +from protorpc import protobuf
|
| +from protorpc import registry
|
| +from protorpc import transport
|
| +from protorpc import util
|
| +
|
| +EXCLUDED_PACKAGES = frozenset(['protorpc.registry',
|
| + 'protorpc.messages',
|
| + 'protorpc.descriptor',
|
| + 'protorpc.message_types',
|
| + ])
|
| +
|
| +commands = {}
|
| +
|
| +
|
| +def usage():
|
| + """Print usage help and exit with an error code."""
|
| + parser.print_help()
|
| + sys.exit(2)
|
| +
|
| +
|
| +def fatal_error(message):
|
| + """Print fatal error messages exit with an error code.
|
| +
|
| + Args:
|
| + message: Message to print to stderr before exit.
|
| + """
|
| + sys.stderr.write(message)
|
| + sys.exit(1)
|
| +
|
| +
|
| +def open_input_file(filename):
|
| + """Open file for reading.
|
| +
|
| + Args:
|
| + filename: Name of input file to open or None to open stdin.
|
| +
|
| + Returns:
|
| + Opened file if string provided, stdin if filename is None.
|
| + """
|
| + # TODO(rafek): Detect missing or invalid files, generating user friendly
|
| + # error messages.
|
| + if filename is None:
|
| + return sys.stdin
|
| + else:
|
| + try:
|
| + return open(filename, 'rb')
|
| + except IOError, err:
|
| + fatal_error(str(err))
|
| +
|
| +
|
| +@util.positional(1)
|
| +def generate_file_descriptor(dest_dir, file_descriptor, force_overwrite):
|
| + """Generate a single file descriptor to destination directory.
|
| +
|
| + Will generate a single Python file from a file descriptor under dest_dir.
|
| + The sub-directory where the file is generated is determined by the package
|
| + name of descriptor.
|
| +
|
| + Descriptors without package names will not be generated.
|
| +
|
| + Descriptors that are part of the ProtoRPC distribution will not be generated.
|
| +
|
| + Args:
|
| + dest_dir: Directory under which to generate files.
|
| + file_descriptor: FileDescriptor instance to generate source code from.
|
| + force_overwrite: If True, existing files will be overwritten.
|
| + """
|
| + package = file_descriptor.package
|
| + if not package:
|
| + # TODO(rafek): Option to cause an error on this condition.
|
| + logging.warn('Will not generate descriptor without package name')
|
| + return
|
| +
|
| + if package in EXCLUDED_PACKAGES:
|
| + logging.warn('Will not generate main ProtoRPC class %s' % package)
|
| + return
|
| +
|
| + package_path = package.split('.')
|
| + directory = package_path[:-1]
|
| + package_file_name = package_path[-1]
|
| + directory_name = os.path.join(dest_dir, *directory)
|
| + output_file_name = os.path.join(directory_name,
|
| + '%s.py' % (package_file_name,))
|
| +
|
| + try:
|
| + os.makedirs(directory_name)
|
| + except OSError, err:
|
| + if err.errno != errno.EEXIST:
|
| + raise
|
| +
|
| + if not force_overwrite and os.path.exists(output_file_name):
|
| + logging.warn('Not overwriting %s with package %s',
|
| + output_file_name, package)
|
| + return
|
| +
|
| + output_file = open(output_file_name, 'w')
|
| +
|
| + logging.info('Writing package %s to %s',
|
| + file_descriptor.package, output_file_name)
|
| + generate_python.format_python_file(file_descriptor, output_file)
|
| +
|
| +
|
| +@util.positional(1)
|
| +def command(name, required=(), optional=()):
|
| + """Decorator used for declaring commands used on command line.
|
| +
|
| + Each command of this tool can have any number of sequential required
|
| + parameters and optional parameters. The required and optional parameters
|
| + will be displayed in the command usage. Arguments passed in to the command
|
| + are checked to ensure they have at least the required parameters and not
|
| + too many parameters beyond the optional ones. When there are not enough
|
| + or too few parameters the usage message is generated and the program exits
|
| + with an error code.
|
| +
|
| + Functions decorated thus are added to commands by their name.
|
| +
|
| + Resulting decorated functions will have required and optional attributes
|
| + assigned to them so that appear in the usage message.
|
| +
|
| + Args:
|
| + name: Name of command that will follow the program name on the command line.
|
| + required: List of required parameter names as displayed in the usage
|
| + message.
|
| + optional: List of optional parameter names as displayed in the usage
|
| + message.
|
| + """
|
| + def check_params_decorator(function):
|
| + def check_params_wrapper(options, *args):
|
| + if not (len(required) <= len(args) <= len(required) + len(optional)):
|
| + sys.stderr.write("Incorrect usage for command '%s'\n\n" % name)
|
| + usage()
|
| + function(options, *args)
|
| + check_params_wrapper.required = required
|
| + check_params_wrapper.optional = optional
|
| + commands[name] = check_params_wrapper
|
| + return check_params_wrapper
|
| + return check_params_decorator
|
| +
|
| +
|
| +@command('file', optional=['input-filename', 'output-filename'])
|
| +def file_command(options, input_filename=None, output_filename=None):
|
| + """Generate a single descriptor file to Python.
|
| +
|
| + Args:
|
| + options: Parsed command line options.
|
| + input_filename: File to read protobuf FileDescriptor from. If None
|
| + will read from stdin.
|
| + output_filename: File to write Python source code to. If None will
|
| + generate to stdout.
|
| + """
|
| + with open_input_file(input_filename) as input_file:
|
| + descriptor_content = input_file.read()
|
| +
|
| + if output_filename:
|
| + output_file = open(output_filename, 'w')
|
| + else:
|
| + output_file = sys.stdout
|
| +
|
| + file_descriptor = protobuf.decode_message(descriptor.FileDescriptor,
|
| + descriptor_content)
|
| + generate_python.format_python_file(file_descriptor, output_file)
|
| +
|
| +
|
| +@command('fileset', optional=['filename'])
|
| +def fileset_command(options, input_filename=None):
|
| + """Generate source directory structure from FileSet.
|
| +
|
| + Args:
|
| + options: Parsed command line options.
|
| + input_filename: File to read protobuf FileSet from. If None will read from
|
| + stdin.
|
| + """
|
| + with open_input_file(input_filename) as input_file:
|
| + descriptor_content = input_file.read()
|
| +
|
| + dest_dir = os.path.expanduser(options.dest_dir)
|
| +
|
| + if not os.path.isdir(dest_dir) and os.path.exists(dest_dir):
|
| + fatal_error("Destination '%s' is not a directory" % dest_dir)
|
| +
|
| + file_set = protobuf.decode_message(descriptor.FileSet,
|
| + descriptor_content)
|
| +
|
| + for file_descriptor in file_set.files:
|
| + generate_file_descriptor(dest_dir, file_descriptor=file_descriptor,
|
| + force_overwrite=options.force)
|
| +
|
| +
|
| +@command('registry',
|
| + required=['host'],
|
| + optional=['service-name', 'registry-path'])
|
| +def registry_command(options,
|
| + host,
|
| + service_name=None,
|
| + registry_path='/protorpc'):
|
| + """Generate source directory structure from remote registry service.
|
| +
|
| + Args:
|
| + options: Parsed command line options.
|
| + host: Web service host where registry service is located. May include
|
| + port.
|
| + service_name: Name of specific service to read. Will generate only Python
|
| + files that service is dependent on. If None, will generate source code
|
| + for all services known by the registry.
|
| + registry_path: Path to find registry if not the default 'protorpc'.
|
| + """
|
| + dest_dir = os.path.expanduser(options.dest_dir)
|
| +
|
| + url = 'http://%s%s' % (host, registry_path)
|
| + reg = registry.RegistryService.Stub(transport.HttpTransport(url))
|
| +
|
| + if service_name is None:
|
| + service_names = [service.name for service in reg.services().services]
|
| + else:
|
| + service_names = [service_name]
|
| +
|
| + file_set = reg.get_file_set(names=service_names).file_set
|
| +
|
| + for file_descriptor in file_set.files:
|
| + generate_file_descriptor(dest_dir, file_descriptor=file_descriptor,
|
| + force_overwrite=options.force)
|
| +
|
| +
|
| +def make_opt_parser():
|
| + """Create options parser with automatically generated command help.
|
| +
|
| + Will iterate over all functions in commands and generate an appropriate
|
| + usage message for them with all their required and optional parameters.
|
| + """
|
| + command_descriptions = []
|
| + for name in sorted(commands.iterkeys()):
|
| + command = commands[name]
|
| + params = ' '.join(['<%s>' % param for param in command.required] +
|
| + ['[<%s>]' % param for param in command.optional])
|
| + command_descriptions.append('%%prog [options] %s %s' % (name, params))
|
| + command_usage = 'usage: %s\n' % '\n '.join(command_descriptions)
|
| +
|
| + parser = optparse.OptionParser(usage=command_usage)
|
| + parser.add_option('-d', '--dest_dir',
|
| + dest='dest_dir',
|
| + default=os.getcwd(),
|
| + help='Write generated files to DIR',
|
| + metavar='DIR')
|
| + parser.add_option('-f', '--force',
|
| + action='store_true',
|
| + dest='force',
|
| + default=False,
|
| + help='Force overwrite of existing files')
|
| + return parser
|
| +
|
| +parser = make_opt_parser()
|
| +
|
| +
|
| +def main():
|
| + # TODO(rafek): Customize verbosity.
|
| + logging.basicConfig(level=logging.INFO)
|
| + options, positional = parser.parse_args()
|
| +
|
| + if not positional:
|
| + usage()
|
| +
|
| + command_name = positional[0]
|
| + command = commands.get(command_name)
|
| + if not command:
|
| + sys.stderr.write("Unknown command '%s'\n\n" % command_name)
|
| + usage()
|
| + parameters = positional[1:]
|
| +
|
| + command(options, *parameters)
|
| +
|
| +
|
| +if __name__ == '__main__':
|
| + main()
|
|
|