| Index: third_party/google-endpoints/apitools/gen/extended_descriptor.py
|
| diff --git a/third_party/google-endpoints/apitools/gen/extended_descriptor.py b/third_party/google-endpoints/apitools/gen/extended_descriptor.py
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..c5d9909be38499277af0ed274113fc1ee69d3a13
|
| --- /dev/null
|
| +++ b/third_party/google-endpoints/apitools/gen/extended_descriptor.py
|
| @@ -0,0 +1,552 @@
|
| +#!/usr/bin/env python
|
| +#
|
| +# Copyright 2015 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.
|
| +
|
| +"""Extended protorpc descriptors.
|
| +
|
| +This takes existing protorpc Descriptor classes and adds extra
|
| +properties not directly supported in proto itself, notably field and
|
| +message descriptions. We need this in order to generate protorpc
|
| +message files with comments.
|
| +
|
| +Note that for most of these classes, we can't simply wrap the existing
|
| +message, since we need to change the type of the subfields. We could
|
| +have a "plain" descriptor attached, but that seems like unnecessary
|
| +bookkeeping. Where possible, we purposely reuse existing tag numbers;
|
| +for new fields, we start numbering at 100.
|
| +"""
|
| +import abc
|
| +import operator
|
| +import textwrap
|
| +
|
| +import six
|
| +
|
| +from apitools.base.protorpclite import descriptor as protorpc_descriptor
|
| +from apitools.base.protorpclite import message_types
|
| +from apitools.base.protorpclite import messages
|
| +import apitools.base.py as apitools_base
|
| +
|
| +
|
| +class ExtendedEnumValueDescriptor(messages.Message):
|
| +
|
| + """Enum value descriptor with additional fields.
|
| +
|
| + Fields:
|
| + name: Name of enumeration value.
|
| + number: Number of enumeration value.
|
| + description: Description of this enum value.
|
| + """
|
| + name = messages.StringField(1)
|
| + number = messages.IntegerField(2, variant=messages.Variant.INT32)
|
| +
|
| + description = messages.StringField(100)
|
| +
|
| +
|
| +class ExtendedEnumDescriptor(messages.Message):
|
| +
|
| + """Enum class descriptor with additional fields.
|
| +
|
| + Fields:
|
| + name: Name of Enum without any qualification.
|
| + values: Values defined by Enum class.
|
| + description: Description of this enum class.
|
| + full_name: Fully qualified name of this enum class.
|
| + enum_mappings: Mappings from python to JSON names for enum values.
|
| + """
|
| +
|
| + class JsonEnumMapping(messages.Message):
|
| +
|
| + """Mapping from a python name to the wire name for an enum."""
|
| + python_name = messages.StringField(1)
|
| + json_name = messages.StringField(2)
|
| +
|
| + name = messages.StringField(1)
|
| + values = messages.MessageField(
|
| + ExtendedEnumValueDescriptor, 2, repeated=True)
|
| +
|
| + description = messages.StringField(100)
|
| + full_name = messages.StringField(101)
|
| + enum_mappings = messages.MessageField(
|
| + 'JsonEnumMapping', 102, repeated=True)
|
| +
|
| +
|
| +class ExtendedFieldDescriptor(messages.Message):
|
| +
|
| + """Field descriptor with additional fields.
|
| +
|
| + Fields:
|
| + field_descriptor: The underlying field descriptor.
|
| + name: The name of this field.
|
| + description: Description of this field.
|
| + """
|
| + field_descriptor = messages.MessageField(
|
| + protorpc_descriptor.FieldDescriptor, 100)
|
| + # We duplicate the names for easier bookkeeping.
|
| + name = messages.StringField(101)
|
| + description = messages.StringField(102)
|
| +
|
| +
|
| +class ExtendedMessageDescriptor(messages.Message):
|
| +
|
| + """Message descriptor with additional fields.
|
| +
|
| + Fields:
|
| + name: Name of Message without any qualification.
|
| + fields: Fields defined for message.
|
| + message_types: Nested Message classes defined on message.
|
| + enum_types: Nested Enum classes defined on message.
|
| + description: Description of this message.
|
| + full_name: Full qualified name of this message.
|
| + decorators: Decorators to include in the definition when printing.
|
| + Printed in the given order from top to bottom (so the last entry
|
| + is the innermost decorator).
|
| + alias_for: This type is just an alias for the named type.
|
| + field_mappings: Mappings from python to json field names.
|
| + """
|
| +
|
| + class JsonFieldMapping(messages.Message):
|
| +
|
| + """Mapping from a python name to the wire name for a field."""
|
| + python_name = messages.StringField(1)
|
| + json_name = messages.StringField(2)
|
| +
|
| + name = messages.StringField(1)
|
| + fields = messages.MessageField(ExtendedFieldDescriptor, 2, repeated=True)
|
| + message_types = messages.MessageField(
|
| + 'extended_descriptor.ExtendedMessageDescriptor', 3, repeated=True)
|
| + enum_types = messages.MessageField(
|
| + ExtendedEnumDescriptor, 4, repeated=True)
|
| +
|
| + description = messages.StringField(100)
|
| + full_name = messages.StringField(101)
|
| + decorators = messages.StringField(102, repeated=True)
|
| + alias_for = messages.StringField(103)
|
| + field_mappings = messages.MessageField(
|
| + 'JsonFieldMapping', 104, repeated=True)
|
| +
|
| +
|
| +class ExtendedFileDescriptor(messages.Message):
|
| +
|
| + """File descriptor with additional fields.
|
| +
|
| + Fields:
|
| + package: Fully qualified name of package that definitions belong to.
|
| + message_types: Message definitions contained in file.
|
| + enum_types: Enum definitions contained in file.
|
| + description: Description of this file.
|
| + additional_imports: Extra imports used in this package.
|
| + """
|
| + package = messages.StringField(2)
|
| +
|
| + message_types = messages.MessageField(
|
| + ExtendedMessageDescriptor, 4, repeated=True)
|
| + enum_types = messages.MessageField(
|
| + ExtendedEnumDescriptor, 5, repeated=True)
|
| +
|
| + description = messages.StringField(100)
|
| + additional_imports = messages.StringField(101, repeated=True)
|
| +
|
| +
|
| +def _WriteFile(file_descriptor, package, version, proto_printer):
|
| + """Write the given extended file descriptor to the printer."""
|
| + proto_printer.PrintPreamble(package, version, file_descriptor)
|
| + _PrintEnums(proto_printer, file_descriptor.enum_types)
|
| + _PrintMessages(proto_printer, file_descriptor.message_types)
|
| + custom_json_mappings = _FetchCustomMappings(
|
| + file_descriptor.enum_types, file_descriptor.package)
|
| + custom_json_mappings.extend(
|
| + _FetchCustomMappings(
|
| + file_descriptor.message_types, file_descriptor.package))
|
| + for mapping in custom_json_mappings:
|
| + proto_printer.PrintCustomJsonMapping(mapping)
|
| +
|
| +
|
| +def WriteMessagesFile(file_descriptor, package, version, printer):
|
| + """Write the given extended file descriptor to out as a message file."""
|
| + _WriteFile(file_descriptor, package, version,
|
| + _Proto2Printer(printer))
|
| +
|
| +
|
| +def WritePythonFile(file_descriptor, package, version, printer):
|
| + """Write the given extended file descriptor to out."""
|
| + _WriteFile(file_descriptor, package, version,
|
| + _ProtoRpcPrinter(printer))
|
| +
|
| +
|
| +def PrintIndentedDescriptions(printer, ls, name, prefix=''):
|
| + if ls:
|
| + with printer.Indent(indent=prefix):
|
| + with printer.CommentContext():
|
| + width = printer.CalculateWidth() - len(prefix)
|
| + printer()
|
| + printer(name + ':')
|
| + for x in ls:
|
| + description = '%s: %s' % (x.name, x.description)
|
| + for line in textwrap.wrap(description, width,
|
| + initial_indent=' ',
|
| + subsequent_indent=' '):
|
| + printer(line)
|
| +
|
| +
|
| +def _FetchCustomMappings(descriptor_ls, package):
|
| + """Find and return all custom mappings for descriptors in descriptor_ls."""
|
| + custom_mappings = []
|
| + for descriptor in descriptor_ls:
|
| + if isinstance(descriptor, ExtendedEnumDescriptor):
|
| + custom_mappings.extend(
|
| + _FormatCustomJsonMapping('Enum', m, descriptor, package)
|
| + for m in descriptor.enum_mappings)
|
| + elif isinstance(descriptor, ExtendedMessageDescriptor):
|
| + custom_mappings.extend(
|
| + _FormatCustomJsonMapping('Field', m, descriptor, package)
|
| + for m in descriptor.field_mappings)
|
| + custom_mappings.extend(
|
| + _FetchCustomMappings(descriptor.enum_types, package))
|
| + custom_mappings.extend(
|
| + _FetchCustomMappings(descriptor.message_types, package))
|
| + return custom_mappings
|
| +
|
| +
|
| +def _FormatCustomJsonMapping(mapping_type, mapping, descriptor, package):
|
| + return '\n'.join((
|
| + 'encoding.AddCustomJson%sMapping(' % mapping_type,
|
| + " %s, '%s', '%s'," % (descriptor.full_name, mapping.python_name,
|
| + mapping.json_name),
|
| + ' package=%r)' % package,
|
| + ))
|
| +
|
| +
|
| +def _EmptyMessage(message_type):
|
| + return not any((message_type.enum_types,
|
| + message_type.message_types,
|
| + message_type.fields))
|
| +
|
| +
|
| +class ProtoPrinter(six.with_metaclass(abc.ABCMeta, object)):
|
| +
|
| + """Interface for proto printers."""
|
| +
|
| + @abc.abstractmethod
|
| + def PrintPreamble(self, package, version, file_descriptor):
|
| + """Print the file docstring and import lines."""
|
| +
|
| + @abc.abstractmethod
|
| + def PrintEnum(self, enum_type):
|
| + """Print the given enum declaration."""
|
| +
|
| + @abc.abstractmethod
|
| + def PrintMessage(self, message_type):
|
| + """Print the given message declaration."""
|
| +
|
| +
|
| +class _Proto2Printer(ProtoPrinter):
|
| +
|
| + """Printer for proto2 definitions."""
|
| +
|
| + def __init__(self, printer):
|
| + self.__printer = printer
|
| +
|
| + def __PrintEnumCommentLines(self, enum_type):
|
| + description = enum_type.description or '%s enum type.' % enum_type.name
|
| + for line in textwrap.wrap(description,
|
| + self.__printer.CalculateWidth() - 3):
|
| + self.__printer('// %s', line)
|
| + PrintIndentedDescriptions(self.__printer, enum_type.values, 'Values',
|
| + prefix='// ')
|
| +
|
| + def __PrintEnumValueCommentLines(self, enum_value):
|
| + if enum_value.description:
|
| + width = self.__printer.CalculateWidth() - 3
|
| + for line in textwrap.wrap(enum_value.description, width):
|
| + self.__printer('// %s', line)
|
| +
|
| + def PrintEnum(self, enum_type):
|
| + self.__PrintEnumCommentLines(enum_type)
|
| + self.__printer('enum %s {', enum_type.name)
|
| + with self.__printer.Indent():
|
| + enum_values = sorted(
|
| + enum_type.values, key=operator.attrgetter('number'))
|
| + for enum_value in enum_values:
|
| + self.__printer()
|
| + self.__PrintEnumValueCommentLines(enum_value)
|
| + self.__printer('%s = %s;', enum_value.name, enum_value.number)
|
| + self.__printer('}')
|
| + self.__printer()
|
| +
|
| + def PrintPreamble(self, package, version, file_descriptor):
|
| + self.__printer('// Generated message classes for %s version %s.',
|
| + package, version)
|
| + self.__printer('// NOTE: This file is autogenerated and should not be '
|
| + 'edited by hand.')
|
| + description_lines = textwrap.wrap(file_descriptor.description, 75)
|
| + if description_lines:
|
| + self.__printer('//')
|
| + for line in description_lines:
|
| + self.__printer('// %s', line)
|
| + self.__printer()
|
| + self.__printer('syntax = "proto2";')
|
| + self.__printer('package %s;', file_descriptor.package)
|
| +
|
| + def __PrintMessageCommentLines(self, message_type):
|
| + """Print the description of this message."""
|
| + description = message_type.description or '%s message type.' % (
|
| + message_type.name)
|
| + width = self.__printer.CalculateWidth() - 3
|
| + for line in textwrap.wrap(description, width):
|
| + self.__printer('// %s', line)
|
| + PrintIndentedDescriptions(self.__printer, message_type.enum_types,
|
| + 'Enums', prefix='// ')
|
| + PrintIndentedDescriptions(self.__printer, message_type.message_types,
|
| + 'Messages', prefix='// ')
|
| + PrintIndentedDescriptions(self.__printer, message_type.fields,
|
| + 'Fields', prefix='// ')
|
| +
|
| + def __PrintFieldDescription(self, description):
|
| + for line in textwrap.wrap(description,
|
| + self.__printer.CalculateWidth() - 3):
|
| + self.__printer('// %s', line)
|
| +
|
| + def __PrintFields(self, fields):
|
| + for extended_field in fields:
|
| + field = extended_field.field_descriptor
|
| + field_type = messages.Field.lookup_field_type_by_variant(
|
| + field.variant)
|
| + self.__printer()
|
| + self.__PrintFieldDescription(extended_field.description)
|
| + label = str(field.label).lower()
|
| + if field_type in (messages.EnumField, messages.MessageField):
|
| + proto_type = field.type_name
|
| + else:
|
| + proto_type = str(field.variant).lower()
|
| + default_statement = ''
|
| + if field.default_value:
|
| + if field_type in [messages.BytesField, messages.StringField]:
|
| + default_value = '"%s"' % field.default_value
|
| + elif field_type is messages.BooleanField:
|
| + default_value = str(field.default_value).lower()
|
| + else:
|
| + default_value = str(field.default_value)
|
| +
|
| + default_statement = ' [default = %s]' % default_value
|
| + self.__printer(
|
| + '%s %s %s = %d%s;',
|
| + label, proto_type, field.name, field.number, default_statement)
|
| +
|
| + def PrintMessage(self, message_type):
|
| + self.__printer()
|
| + self.__PrintMessageCommentLines(message_type)
|
| + if _EmptyMessage(message_type):
|
| + self.__printer('message %s {}', message_type.name)
|
| + return
|
| + self.__printer('message %s {', message_type.name)
|
| + with self.__printer.Indent():
|
| + _PrintEnums(self, message_type.enum_types)
|
| + _PrintMessages(self, message_type.message_types)
|
| + self.__PrintFields(message_type.fields)
|
| + self.__printer('}')
|
| +
|
| + def PrintCustomJsonMapping(self, mapping_lines):
|
| + raise NotImplementedError(
|
| + 'Custom JSON encoding not supported for proto2')
|
| +
|
| +
|
| +class _ProtoRpcPrinter(ProtoPrinter):
|
| +
|
| + """Printer for ProtoRPC definitions."""
|
| +
|
| + def __init__(self, printer):
|
| + self.__printer = printer
|
| +
|
| + def __PrintClassSeparator(self):
|
| + self.__printer()
|
| + if not self.__printer.indent:
|
| + self.__printer()
|
| +
|
| + def __PrintEnumDocstringLines(self, enum_type):
|
| + description = enum_type.description or '%s enum type.' % enum_type.name
|
| + for line in textwrap.wrap('"""%s' % description,
|
| + self.__printer.CalculateWidth()):
|
| + self.__printer(line)
|
| + PrintIndentedDescriptions(self.__printer, enum_type.values, 'Values')
|
| + self.__printer('"""')
|
| +
|
| + def PrintEnum(self, enum_type):
|
| + self.__printer('class %s(_messages.Enum):', enum_type.name)
|
| + with self.__printer.Indent():
|
| + self.__PrintEnumDocstringLines(enum_type)
|
| + enum_values = sorted(
|
| + enum_type.values, key=operator.attrgetter('number'))
|
| + for enum_value in enum_values:
|
| + self.__printer('%s = %s', enum_value.name, enum_value.number)
|
| + if not enum_type.values:
|
| + self.__printer('pass')
|
| + self.__PrintClassSeparator()
|
| +
|
| + def __PrintAdditionalImports(self, imports):
|
| + """Print additional imports needed for protorpc."""
|
| + google_imports = [x for x in imports if 'google' in x]
|
| + other_imports = [x for x in imports if 'google' not in x]
|
| + if other_imports:
|
| + for import_ in sorted(other_imports):
|
| + self.__printer(import_)
|
| + self.__printer()
|
| + # Note: If we ever were going to add imports from this package, we'd
|
| + # need to sort those out and put them at the end.
|
| + if google_imports:
|
| + for import_ in sorted(google_imports):
|
| + self.__printer(import_)
|
| + self.__printer()
|
| +
|
| + def PrintPreamble(self, package, version, file_descriptor):
|
| + self.__printer('"""Generated message classes for %s version %s.',
|
| + package, version)
|
| + self.__printer()
|
| + for line in textwrap.wrap(file_descriptor.description, 78):
|
| + self.__printer(line)
|
| + self.__printer('"""')
|
| + self.__printer('# NOTE: This file is autogenerated and should not be '
|
| + 'edited by hand.')
|
| + self.__printer()
|
| + self.__PrintAdditionalImports(file_descriptor.additional_imports)
|
| + self.__printer()
|
| + self.__printer("package = '%s'", file_descriptor.package)
|
| + self.__printer()
|
| + self.__printer()
|
| +
|
| + def __PrintMessageDocstringLines(self, message_type):
|
| + """Print the docstring for this message."""
|
| + description = message_type.description or '%s message type.' % (
|
| + message_type.name)
|
| + short_description = (
|
| + _EmptyMessage(message_type) and
|
| + len(description) < (self.__printer.CalculateWidth() - 6))
|
| + with self.__printer.CommentContext():
|
| + if short_description:
|
| + # Note that we use explicit string interpolation here since
|
| + # we're in comment context.
|
| + self.__printer('"""%s"""' % description)
|
| + return
|
| + for line in textwrap.wrap('"""%s' % description,
|
| + self.__printer.CalculateWidth()):
|
| + self.__printer(line)
|
| +
|
| + PrintIndentedDescriptions(self.__printer, message_type.enum_types,
|
| + 'Enums')
|
| + PrintIndentedDescriptions(
|
| + self.__printer, message_type.message_types, 'Messages')
|
| + PrintIndentedDescriptions(
|
| + self.__printer, message_type.fields, 'Fields')
|
| + self.__printer('"""')
|
| + self.__printer()
|
| +
|
| + def PrintMessage(self, message_type):
|
| + if message_type.alias_for:
|
| + self.__printer(
|
| + '%s = %s', message_type.name, message_type.alias_for)
|
| + self.__PrintClassSeparator()
|
| + return
|
| + for decorator in message_type.decorators:
|
| + self.__printer('@%s', decorator)
|
| + self.__printer('class %s(_messages.Message):', message_type.name)
|
| + with self.__printer.Indent():
|
| + self.__PrintMessageDocstringLines(message_type)
|
| + _PrintEnums(self, message_type.enum_types)
|
| + _PrintMessages(self, message_type.message_types)
|
| + _PrintFields(message_type.fields, self.__printer)
|
| + self.__PrintClassSeparator()
|
| +
|
| + def PrintCustomJsonMapping(self, mapping):
|
| + self.__printer(mapping)
|
| +
|
| +
|
| +def _PrintEnums(proto_printer, enum_types):
|
| + """Print all enums to the given proto_printer."""
|
| + enum_types = sorted(enum_types, key=operator.attrgetter('name'))
|
| + for enum_type in enum_types:
|
| + proto_printer.PrintEnum(enum_type)
|
| +
|
| +
|
| +def _PrintMessages(proto_printer, message_list):
|
| + message_list = sorted(message_list, key=operator.attrgetter('name'))
|
| + for message_type in message_list:
|
| + proto_printer.PrintMessage(message_type)
|
| +
|
| +
|
| +_MESSAGE_FIELD_MAP = {
|
| + message_types.DateTimeMessage.definition_name(): (
|
| + message_types.DateTimeField),
|
| +}
|
| +
|
| +
|
| +def _PrintFields(fields, printer):
|
| + for extended_field in fields:
|
| + field = extended_field.field_descriptor
|
| + printed_field_info = {
|
| + 'name': field.name,
|
| + 'module': '_messages',
|
| + 'type_name': '',
|
| + 'type_format': '',
|
| + 'number': field.number,
|
| + 'label_format': '',
|
| + 'variant_format': '',
|
| + 'default_format': '',
|
| + }
|
| +
|
| + message_field = _MESSAGE_FIELD_MAP.get(field.type_name)
|
| + if message_field:
|
| + printed_field_info['module'] = '_message_types'
|
| + field_type = message_field
|
| + elif field.type_name == 'extra_types.DateField':
|
| + printed_field_info['module'] = 'extra_types'
|
| + field_type = apitools_base.DateField
|
| + else:
|
| + field_type = messages.Field.lookup_field_type_by_variant(
|
| + field.variant)
|
| +
|
| + if field_type in (messages.EnumField, messages.MessageField):
|
| + printed_field_info['type_format'] = "'%s', " % field.type_name
|
| +
|
| + if field.label == protorpc_descriptor.FieldDescriptor.Label.REQUIRED:
|
| + printed_field_info['label_format'] = ', required=True'
|
| + elif field.label == protorpc_descriptor.FieldDescriptor.Label.REPEATED:
|
| + printed_field_info['label_format'] = ', repeated=True'
|
| +
|
| + if field_type.DEFAULT_VARIANT != field.variant:
|
| + printed_field_info['variant_format'] = (
|
| + ', variant=_messages.Variant.%s' % field.variant)
|
| +
|
| + if field.default_value:
|
| + if field_type in [messages.BytesField, messages.StringField]:
|
| + default_value = repr(field.default_value)
|
| + elif field_type is messages.EnumField:
|
| + try:
|
| + default_value = str(int(field.default_value))
|
| + except ValueError:
|
| + default_value = repr(field.default_value)
|
| + else:
|
| + default_value = field.default_value
|
| +
|
| + printed_field_info[
|
| + 'default_format'] = ', default=%s' % (default_value,)
|
| +
|
| + printed_field_info['type_name'] = field_type.__name__
|
| + args = ''.join('%%(%s)s' % field for field in (
|
| + 'type_format',
|
| + 'number',
|
| + 'label_format',
|
| + 'variant_format',
|
| + 'default_format'))
|
| + format_str = '%%(name)s = %%(module)s.%%(type_name)s(%s)' % args
|
| + printer(format_str % printed_field_info)
|
|
|