예제 #1
0
  def link_command(self, command):
    """Links the schema Command."""

    if not isinstance(command, schema.Command):
      raise exception.InvalidType('Expecting a command')

    options = command.desc.options.Extensions[wdl_options_pb2.command]
    if options.completion_event:
      response_desc = command.desc.parent.messages[options.completion_event]
      if not response_desc:
        raise exception.InvalidType('Cannot find completion event %s in trait' %
                                    options.completion_event)
      response = self.get_obj(response_desc.full_name)
      if not isinstance(response, schema.CommandResponse):
        raise exception.InvalidType(
            'Completion event %s must be a ResponseEvent' %
            response_desc.full_name)
      command.response = response
      command.response.parent = command

    if options.compatibility.HasField('min_version'):
      command.min_version = options.compatibility.min_version
    if options.compatibility.HasField('max_version'):
      command.max_version = options.compatibility.max_version

    for field_desc in command.desc.fields.values():
      command.parameter_list.append(self.get_obj(field_desc.full_name))

    command.extends = self.get_extends(
        command.desc.options.Extensions[wdl_options_pb2.command].extends)
예제 #2
0
  def link_typespace(self, typespace):
    """Links and initializes a given schema typespace.

    Args:
      typespace: Uninitialized typespace object

    Raises:
      InvalidType:
      MissingArgument:
    """

    if not isinstance(typespace, schema.Typespace):
      raise exception.InvalidType('Expecting an typespace')

    if typespace.desc.fields:
      raise exception.InvalidType(
          'Typespace {} contains properties, typespaces should not contain '
          'properties.'.format(typespace.full_name))

    options = typespace.desc.options.Extensions[wdl_options_pb2.typespace]

    vendor = self.get_vendor(typespace.desc.full_name)
    vendor.typespace_list.append(typespace)

    # Used to set the file descriptor in dev_*
    typespace.nwv_pb_desc = text_encoding.CEscape(
        typespace.desc.SerializeToString(), False)

    typespace.stability = schema.Stability(options.stability)
    typespace.version = options.version
    typespace.version_map = schema.VersionMap(options.version_map)

    for nested_msg_desc in _order_messages_by_dependency(
        typespace.desc.messages.values(), typespace.full_name):
      if nested_msg_desc.is_map_entry:
        continue  # ignore map entries
      nested_msg = self.get_obj(nested_msg_desc.full_name)
      if nested_msg:
        if isinstance(nested_msg, schema.Command):
          typespace.command_list.append(nested_msg)
        elif isinstance(nested_msg, schema.Event):
          typespace.event_list.append(nested_msg)
        elif isinstance(nested_msg, schema.Struct):
          typespace.struct_list.append(nested_msg)
        else:
          raise exception.InvalidType('Unexpected type in typespace')

    for nested_enum_desc in typespace.desc.enums.values():
      nested_enum = self.get_obj(nested_enum_desc.full_name)
      constant_type = nested_enum_desc.options.Extensions[
          wdl_options_pb2.enumopts].constant_type
      if constant_type:
        typespace.constant_group_list.append(nested_enum)
      else:
        typespace.enum_list.append(nested_enum)
예제 #3
0
  def link_struct(self, struct):
    """Parse a protobuf message and turn it into a gwv schema struct.

    Args:
      struct: Protobuf message descriptor representing a struct

    Raises:
      InvalidType: Encountered an invalid type while linking
    """

    options = struct.desc.options.Extensions[wdl_options_pb2.structopts]

    if struct.desc.is_map_entry:
      # ignore map entries
      return

    if self.is_common(struct.full_name):
      self.link_common_struct(struct)
    elif (not struct.desc.parent or not isinstance(
        self.get_obj(struct.desc.parent.full_name),
        schema.StructEnumCollectionBase)):
      raise exception.InvalidType((
          'Unexpected struct %s defined outside a typespace or trait' % struct))

    for field_desc in struct.desc.fields.values():
      struct.field_list.append(self.get_obj(field_desc.full_name))

    struct.extends = self.get_extends(
        struct.desc.options.Extensions[wdl_options_pb2.structopts].extends)

    if options is not None:
      if options.compatibility.HasField('min_version'):
        struct.min_version = options.compatibility.min_version
      if options.compatibility.HasField('max_version'):
        struct.max_version = options.compatibility.max_version
예제 #4
0
  def link_file(self, file_obj):
    """Links and initializes a given schema file.

    Args:
      file_obj: Uninitialized file_obj object

    Raises:
      InvalidType:
    """

    if not isinstance(file_obj, schema.File):
      raise exception.InvalidType('Expecting a file')

    if not self.is_protobuf(file_obj.full_name):
      vendor = self.get_vendor(file_obj.full_name)
      if vendor != None:
          vendor.file_list.append(file_obj)

    for message_desc in file_obj.desc.message_type:
      full_name = '.'.join((file_obj.desc.package, message_desc.name))
      schema_obj = self.get_obj(full_name)
      if isinstance(schema_obj, schema.Trait):
        file_obj.trait_list.append(schema_obj)
      elif isinstance(schema_obj, schema.Typespace):
        file_obj.typespace_list.append(schema_obj)
      elif isinstance(schema_obj, schema.Interface):
        file_obj.interface_list.append(schema_obj)
      elif isinstance(schema_obj, schema.Resource):
        file_obj.resource_list.append(schema_obj)
      elif isinstance(schema_obj, schema.Struct):
        file_obj.struct_list.append(schema_obj)
      else:
        raise exception.InvalidType('Unexpected type in file: {}'.format(
            type(schema_obj)))

    for enum_desc in file_obj.desc.enum_type:
      full_name = '.'.join((file_obj.desc.package, enum_desc.name))
      schema_obj = self.get_obj(full_name)
      if isinstance(schema_obj, schema.Enum):
        file_obj.enum_list.append(schema_obj)
      else:
        raise exception.InvalidType('Unexpected type in file: {}'.format(
            type(schema_obj)))
예제 #5
0
  def link_enum(self, enum):
    """Parses the protobuf enum descriptor turns into a Google Weave Enum.

    Args:
      enum: A protobuf enum description to add to schema

    Raises:
      InvalidType: Encountered an invalid type while linking
      DuplicateObject: Duplicate enum value pair encountered
      InvalidUsage: Option used in an invalid way
    """

    options = enum.desc.options.Extensions[wdl_options_pb2.enumopts]
    enum_pairs = enum.pair_list

    if self.is_common(enum.full_name):
      self.link_common_enum(enum)
    elif (not enum.desc.parent or not isinstance(
        self.get_obj(enum.desc.parent.full_name),
        schema.StructEnumCollectionBase)):
      raise exception.InvalidType(
          ('Unexpected enum %s defined outside a typespace or trait' % enum))

    if options is not None:
      if options.compatibility.HasField('min_version'):
        enum.min_version = options.compatibility.min_version
      if options.compatibility.HasField('max_version'):
        enum.max_version = options.compatibility.max_version

    for value in enum.desc.values.values():
      description = self.parse_comments(value)
      pair_opts = value.options.Extensions[wdl_options_pb2.enumvalue]

      value.full_name.replace(
          inflection.underscore(enum.base_name).decode('utf-8').upper() + '_',
          '')

      enum_pair = schema.EnumPair(value.full_name, value.number, description)
      enum_pair.source_file = enum.source_file

      if pair_opts is not None:
        if pair_opts.compatibility.HasField('min_version'):
          enum_pair.min_version = pair_opts.compatibility.min_version
        if pair_opts.compatibility.HasField('max_version'):
          enum_pair.max_version = pair_opts.compatibility.max_version

      try:
        enum_pairs.append(enum_pair)
      except exception.DuplicateObject:
        pass

    enum.is_bitmask = options.bitmask

    if options.extends:
      raise exception.InvalidUsage('Extending enums is not yet supported.')
예제 #6
0
  def link_command_response(self, response):
    """Links the schema CommandResponse."""

    if not isinstance(response, schema.CommandResponse):
      raise exception.InvalidType('Expecting a CommandResponse')

    options = response.desc.options.Extensions[wdl_options_pb2.event]

    if options.compatibility.HasField('min_version'):
      response.min_version = options.compatibility.min_version
    if options.compatibility.HasField('max_version'):
      response.max_version = options.compatibility.max_version

    for field_desc in response.desc.fields.values():
      response.field_list.append(self.get_obj(field_desc.full_name))
예제 #7
0
  def link_event(self, event):
    """Links the schema Event."""

    if not isinstance(event, schema.Event):
      raise exception.InvalidType('Expecting an event')

    options = event.desc.options.Extensions[wdl_options_pb2.event]

    for field_desc in event.desc.fields.values():
      event.field_list.append(self.get_obj(field_desc.full_name))

    if options.compatibility.HasField('min_version'):
      event.min_version = options.compatibility.min_version
    if options.compatibility.HasField('max_version'):
      event.max_version = options.compatibility.max_version

    event.extends = self.get_extends(options.extends)
    event.importance = event.Importance(options.event_importance)
예제 #8
0
  def link_constant_group(self, enum):
    """Links the schema ConstantGroup."""

    # NOTE: Throws ValueError if constant_type is invalid

    if not isinstance(enum, schema.ConstantGroup):
      raise exception.InvalidType('Expecting a constant group')

    constant_type = schema.Constant.Type(
        enum.desc.options.Extensions[wdl_options_pb2.enumopts].constant_type)

    constants = enum.constant_list

    for value in enum.desc.values.values():
      description = self.parse_comments(value)

      # For now, require exactly 1 value
      constant_value, = (
          value.options.Extensions[wdl_options_pb2.enumvalue]
          .constant_resource_id)

      constants.append(
          schema.Constant(value.full_name, value.number, description,
                          constant_type, constant_value))
예제 #9
0
  def link_resource_component(self, component):
    """Link and initialize an nwv resource component.

    Args:
      component: Uninitialized component objcet
    """

    options = component.desc.options
    if options.HasExtension(wdl_options_pb2.traitinst):
      instance_id = options.Extensions[wdl_options_pb2.traitinst].instance
      component.instance_id = instance_id
    if not options.HasExtension(wdl_options_pb2.traitconfig):
      raise exception.InvalidUsage(
          'Trait config missing on {}. Every trait instance '
          'component must define trait config options.'.format(
              component.full_name))
    config_options = options.Extensions[wdl_options_pb2.traitconfig]
    component.published_by = schema.ResourceComponent.PublishedBy(
        config_options.published_by)
    if component.published_by == schema.ResourceComponent.PublishedBy.SELF:
      if not config_options.HasField('proxied'):
        raise exception.InvalidUsage(
            'Trait component {} is published by SELF but does not '
            'explicitly define proxied.'.format(component.full_name))
    elif (component.published_by ==
          schema.ResourceComponent.PublishedBy.EXTERNAL):
      if not config_options.HasField('subscribed'):
        raise exception.InvalidUsage(
            'Trait component {} is published by EXTERNAL but does '
            'not explicitly define subscribed.'.format(component.full_name))
    component.subscribed = config_options.subscribed
    component.proxied = config_options.proxied
    component.trait = self.get_obj(component.desc.type_name)

    if config_options.HasField('min_version'):
      component.min_version = config_options.min_version

    refinement_options = config_options.prop_refinement
    for refinement_option in refinement_options:
      prop_name = refinement_option.property
      prop = component.trait.state_list.by_name(prop_name)
      if not prop:
        raise exception.InvalidType('Property {} does not exist on trait {}'
                                    .format(prop_name, component.full_name))
      refinement = component.property_refinements[
          prop_name] = schema.FieldRefinement(prop)
      refinement.implemented = not refinement_option.unimplemented
      field_desc = refinement.field.desc
      field_type = WRAPPER_TYPES.get(field_desc.type_name, field_desc.type)
      if refinement_option.initial_bool_value:
        if field_type != field_desc.TYPE_BOOL:
          raise exception.InvalidType(
              'Inital value for for {} on {} is type {}, expected {}'.format(
                  prop_name, component.full_name, field_desc.type, 'BOOL'))
        refinement.initial_value = refinement_option.initial_bool_value
      if refinement_option.initial_int_value:
        if field_type not in (field_desc.TYPE_INT32, field_desc.TYPE_INT64):
          raise exception.InvalidType(
              'Inital value for for {} on {} is type {}, expected {}'.format(
                  prop_name, component.full_name, field_desc.type,
                  'INT32 or INT64'))
        refinement.initial_value = refinement_option.initial_int_value
      if refinement_option.initial_uint_value:
        if field_type not in (field_desc.TYPE_UINT32, field_desc.TYPE_UINT64):
          raise exception.InvalidType(
              'Inital value for for {} on {} is type {}, expected {}'.format(
                  prop_name, component.full_name, field_desc.type,
                  'UINT32 or UINT64'))
        refinement.initial_value = refinement_option.initial_uint_value
      if refinement_option.initial_number_value:
        if field_type not in (field_desc.TYPE_FLOAT, field_desc.TYPE_DOUBLE):
          raise exception.InvalidType(
              'Inital value for for {} on {} is type {}, expected {}'.format(
                  prop_name, component.full_name, field_desc.type,
                  'FLOAT or DOUBLE'))
        refinement.initial_value = refinement_option.initial_number_value
      if refinement_option.initial_string_value:
        if field_type != field_desc.TYPE_STRING:
          raise exception.InvalidType(
              'Inital value for for {} on {} is type {}, expected {}'.format(
                  prop_name, component.full_name, field_desc.type, 'STRING'))
        refinement.initial_value = refinement_option.initial_string_value
      if refinement_option.initial_bytes_base16_value:
        if field_type != field_desc.TYPE_BYTES:
          raise exception.InvalidType(
              'Inital value for for {} on {} is type {}, expected {}'.format(
                  prop_name, component.full_name, field_desc.type, 'BYTES'))
        value = refinement_option.initial_bytes_base16_value
        refinement.initial_bytes_base16_value = value
      if refinement_option.initial_resource_id_value:
        if field_desc.type_name != '.weave.common.ResourceId':
          raise exception.InvalidType(
              'Inital value for for {} on {} is type {}, expected {}'.format(
                  prop_name, component.full_name, field_desc.component,
                  'weave.common.ResourceId'))
        refinement.initial_value = refinement_option.initial_resource_id_value
      if refinement_option.initial_duration_value:
        if field_desc.type_name != '.google.protobuf.Duration':
          raise exception.InvalidType(
              'Inital value for for {} on {} is type {}, expected {}'.format(
                  prop_name, component.full_name, field_desc.type_name,
                  'google.protobuf.Duration'))
        refinement.initial_value = [{
            'seconds': o.seconds,
            'nanos': o.nanos
        } for o in refinement_option.initial_duration_value]
      if refinement_option.initial_timestamp_value:
        if field_desc.type_name != '.google.protobuf.Timestamp':
          raise exception.InvalidType(
              'Inital value for for {} on {} is type {}, expected {}'.format(
                  prop_name, component.full_name, field_desc.type_name,
                  'google.protobuf.Timestamp'))
        refinement.initial_value = [{
            'seconds': o.seconds,
            'nanos': o.nanos
        } for o in refinement_option.initial_timestamp_value]
      if refinement_option.initial_enum_value_name:
        if field_type != field_desc.TYPE_ENUM:
          raise exception.InvalidType(
              'Inital value for for {} on {} is type {}, expected {}'.format(
                  prop_name, component.full_name, field_desc.type_name, 'ENUM'))
        refinement.initial_value = refinement_option.initial_enum_value_name
      if refinement_option.initial_struct_value:
        raise exception.InvalidUsage(
            'Component {} defines an initial value; initial values for'
            ' structs is not implemented.'.format(component.full_name))

      # Every initial value option is an array
      # Flatten the array if the target property is not an array
      if (refinement.initial_value and
          field_desc.label != field_desc.LABEL_REPEATED):
        if len(refinement.initial_value) > 1:
          raise exception.InvalidType(
              'Array of initial values given for {}, which is not'
              ' an array'.format(component.full_name))
        refinement.initial_value = refinement.initial_value[0]
예제 #10
0
  def link_trait(self, trait):
    """Links a given trait and inserts it into the schema.

    Args:
      trait: An uninitialized schema trait object

    Raises:
      InvalidType:
      MissingArgument:
    """

    if not isinstance(trait, schema.Trait):
      raise exception.InvalidType('Expecting an trait')

    options = trait.desc.options.Extensions[wdl_options_pb2.trait]

    vendor = self.get_vendor(trait.desc.full_name)
    vendor.trait_list.append(trait)

    # Used to set the file descriptor in dev_*
    trait.nwv_pb_desc = text_encoding.CEscape(trait.desc.SerializeToString(),
                                              False)

    trait.stability = schema.Stability(options.stability)
    trait.version = options.version
    trait.version_map = schema.VersionMap(options.version_map)

    for field_desc in trait.desc.fields.values():
      trait.state_list.append(self.get_obj(field_desc.full_name))

    for nested_msg_desc in _order_messages_by_dependency(
        trait.desc.messages.values(), trait.full_name):
      if nested_msg_desc.is_map_entry:
        continue  # ignore map entries
      nested_msg = self.get_obj(nested_msg_desc.full_name)
      if isinstance(nested_msg, schema.Command):
        trait.command_list.append(nested_msg)
      elif isinstance(nested_msg, schema.Event):
        trait.event_list.append(nested_msg)
      elif isinstance(nested_msg, schema.CommandResponse):
        pass  # ignore, it will be handled inside link_command
      elif isinstance(nested_msg, schema.Struct):
        trait.struct_list.append(nested_msg)
      else:
        raise exception.InvalidType('Unexpected nested type in trait')

    for nested_enum_desc in trait.desc.enums.values():
      nested_enum = self.get_obj(nested_enum_desc.full_name)
      constant_type = nested_enum_desc.options.Extensions[
          wdl_options_pb2.enumopts].constant_type
      if constant_type:
        trait.constant_group_list.append(nested_enum)
      else:
        trait.enum_list.append(nested_enum)

    trait.extends = self.get_extends(
        trait.desc.options.Extensions[wdl_options_pb2.properties].extends.trait)

    self.validate_extendable(
        trait.desc.options.Extensions[wdl_options_pb2.properties],
        trait.state_list, trait.extends.desc.fields if trait.extends else {})
예제 #11
0
  def parse_options(self, field, options):
    """Parses field constraint options."""

    encoding_val = field.desc.options.Extensions[wdl_options_pb2.tlv].encoding
    tlv_encoding_fixed = wdl_options_pb2.Encoding.Name(encoding_val) == 'FIXED'

    if not options:
      return

    if options.HasField('number_constraints'):
      if field.data_type not in (schema.Field.DataType.FLOAT,
                                 schema.Field.DataType.DOUBLE):
        raise exception.InvalidType(
            'Field %s in %s specifies number constraints, but number '
            'constraints are only valid for floats and doubles.' %
            (field.base_name, field.parent.full_name))
      constraint = options.number_constraints
      if constraint.HasField('min'):
        field.min_value = constraint.min
      if constraint.HasField('max'):
        field.max_value = constraint.max
      if constraint.HasField('precision'):
        field.precision = constraint.precision
      if constraint.HasField('fixed_encoding_width'):
        field.fixed_width = constraint.fixed_encoding_width
      field.is_signed = (field.min_value < 0)

      if not tlv_encoding_fixed:
        raise exception.InvalidType(
            'Field %s in %s has number constraints, but tlv fixed '
            'encoding is not set.' % (field.base_name, field.parent.full_name))
    elif tlv_encoding_fixed:
      raise exception.InvalidType(
          'Field %s in %s has tlv encoding set to fixed but no '
          'number constraints.' % (field.base_name, field.parent.full_name))

    if options.HasField('int_constraints'):
      if field.data_type not in (schema.Field.DataType.INT32,
                                 schema.Field.DataType.INT64):
        raise exception.InvalidType(
            'Field %s in %s specifies int constraints, but int '
            'constraints are only valid for int32 and int64.' %
            (field.base_name, field.parent.full_name))
      constraint = options.int_constraints
      if constraint.HasField('min'):
        field.min_value = constraint.min
      if constraint.HasField('max'):
        field.max_value = constraint.max
      if constraint.HasField('width'):
        field.fixed_width = constraint.width
      field.is_signed = True

    if options.HasField('uint_constraints'):
      if field.data_type not in (schema.Field.DataType.UINT32,
                                 schema.Field.DataType.UINT64):
        raise exception.InvalidType(
            'Field %s in %s specifies uint constraints, but uint '
            'constraints are only valid for uint32 and uint64.' %
            (field.base_name, field.parent.full_name))
      constraint = options.uint_constraints
      if constraint.HasField('min'):
        field.min_value = constraint.min
      if constraint.HasField('max'):
        field.max_value = constraint.max
      if constraint.HasField('width'):
        field.fixed_width = constraint.width
      field.is_signed = False

    if options.HasField('string_constraints'):
      if (field.data_type is not schema.Field.DataType.STRING and
          not field.desc.type_name.endswith('weave.common.StringRef')):
        raise exception.InvalidType(
            'Field %s in %s specifies string constraints, but string '
            'constraints are only valid for strings.' %
            (field.base_name, field.parent.full_name))
      constraint = options.string_constraints
      if constraint.HasField('min_length'):
        field.min_length = constraint.min_length
      if constraint.HasField('max_length'):
        field.max_length = constraint.max_length

    if options.HasField('bytes_constraints'):
      if field.data_type is not schema.Field.DataType.BYTES:
        raise exception.InvalidType(
            'Field %s in %s specifies bytes constraints, but bytes '
            'constraints are only valid for bytes.' % (field.base_name,
                                                       field.parent.full_name))
      constraint = options.bytes_constraints
      if constraint.HasField('min_length'):
        field.min_length = constraint.min_length
      if constraint.HasField('max_length'):
        field.max_length = constraint.max_length

    if options.HasField('timestamp_constraints'):
      if not field.desc.type_name.endswith('google.protobuf.Timestamp'):
        raise exception.InvalidType(
            'Field %s in %s specifies timestamp constraints, but '
            'timestamp constraints are only valid for timestamps.' %
            (field.base_name, field.parent.full_name))
      constraint = options.timestamp_constraints
      if constraint.HasField('signed'):
        field.is_signed = constraint.signed
      if constraint.HasField('precision'):
        field.precision = constraint.precision
      if constraint.HasField('width'):
        field.fixed_width = constraint.width

    if options.HasField('duration_constraints'):
      if not field.desc.type_name.endswith('google.protobuf.Duration'):
        raise exception.InvalidType(
            'Field %s in %s specifies duration constraints, but '
            'duration constraints are only valid for durations.' %
            (field.base_name, field.parent.full_name))
      constraint = options.duration_constraints
      if constraint.HasField('signed'):
        field.is_signed = constraint.signed
      if constraint.HasField('precision'):
        field.precision = constraint.precision
      if constraint.HasField('width'):
        field.fixed_width = constraint.width

    if options.HasField('resource_type'):
      if not field.desc.type_name.endswith('weave.common.ResourceId'):
        raise exception.InvalidType(
            'Field %s in %s specifies resource_type constraints, but '
            'resoruce type is only valid for resourceIds.' %
            (field.base_name, field.parent.full_name))

      field.resource_type = getattr(
          field.ResourceType,
          resource_type_pb2.ResourceType.Name(
              options.resource_type)[len('RESOURCE_TYPE_'):])
예제 #12
0
  def link_field(self, field):
    """Links a schema field object.

    Args:
        field: Unintialized schema field object
    """

    options = None
    if field.desc.options.HasExtension(wdl_options_pb2.prop):
      options = field.desc.options.Extensions[wdl_options_pb2.prop]
    elif field.desc.options.HasExtension(wdl_options_pb2.param):
      options = field.desc.options.Extensions[wdl_options_pb2.param]

    parent_writable = True
    if field.desc.parent.options.HasExtension(wdl_options_pb2.properties):
      props = field.desc.parent.options.Extensions[wdl_options_pb2.properties]
      if props.HasField('writable'):
        parent_writable = (
            wdl_options_pb2.WriteAccess.Name(props.writable) == 'READ_WRITE')

    field.objc_class_prefix = field.desc.file.options.objc_class_prefix
    field.java_outer_classname = field.desc.file.options.java_outer_classname
    field.source_file = field.desc.file.name
    field.is_oneof = field.desc.is_oneof()
    if options:
      field.is_nullable = options.nullable

    field.writable = parent_writable
    if field.desc.options.HasExtension(wdl_options_pb2.prop):
      props = field.desc.options.Extensions[wdl_options_pb2.prop]
      if options.HasField('writable'):
        field.writable = (
            wdl_options_pb2.WriteAccess.Name(props.writable) == 'READ_WRITE')
      field.is_optional = options.optional
      field.is_ephemeral = options.ephemeral

    if options is not None:
      if options.compatibility.HasField('min_version'):
        field.min_version = options.compatibility.min_version
      if options.compatibility.HasField('max_version'):
        field.max_version = options.compatibility.max_version

    field_desc = field.desc
    if field_desc.is_map():
      # Unwrap maps to value field
      field_desc = field.desc.message_type.fields['value']
      field.map_key = self.get_obj(
          field.desc.message_type.fields['key'].full_name)
      self.parse_options(field.map_key,
                         field.desc.options.Extensions[wdl_options_pb2.keyprop])
      field.map_value = self.get_obj(
          field.desc.message_type.fields['value'].full_name)
      self.parse_options(field.map_value, options)
      field.is_map = True
    else:
      field.is_array = field.desc.label == field_desc.LABEL_REPEATED

    field_type = field_desc.type
    if field_desc.type_name in WRAPPER_TYPES:
      field_type = WRAPPER_TYPES[field_desc.type_name]
      if not field.is_nullable and not self.is_common(field.full_name):
        # The fact that this was wrapped is lost,  so can't be checked in
        # separate validator
        raise exception.InvalidUsage(
            'Field %s is a wrapper type '
            'but is not nullable.' % field_desc.full_name)
    elif field.is_nullable and field_type != field_desc.TYPE_MESSAGE:
      raise exception.InvalidUsage('Field %s is nullable, but '
                                   'is not a wrapper or strudct '
                                   'type.' % field_desc.full_name)
    field.data_type = {
        field_desc.TYPE_FLOAT: schema.Field.DataType.FLOAT,
        field_desc.TYPE_DOUBLE: schema.Field.DataType.DOUBLE,
        field_desc.TYPE_UINT64: schema.Field.DataType.UINT64,
        field_desc.TYPE_UINT32: schema.Field.DataType.UINT32,
        field_desc.TYPE_INT64: schema.Field.DataType.INT64,
        field_desc.TYPE_INT32: schema.Field.DataType.INT32,
        field_desc.TYPE_FIXED64: schema.Field.DataType.INT64,
        field_desc.TYPE_FIXED32: schema.Field.DataType.INT32,
        field_desc.TYPE_SINT64: schema.Field.DataType.INT64,
        field_desc.TYPE_SINT32: schema.Field.DataType.INT32,
        field_desc.TYPE_STRING: schema.Field.DataType.STRING,
        field_desc.TYPE_ENUM: schema.Field.DataType.ENUM,
        field_desc.TYPE_BOOL: schema.Field.DataType.BOOL,
        field_desc.TYPE_BYTES: schema.Field.DataType.BYTES,
        field_desc.TYPE_MESSAGE: schema.Field.DataType.STRUCT,
    }[field_type]

    if field.data_type is schema.Field.DataType.STRUCT:
      field.struct_type = self.get_obj(field_desc.type_name)
      if not isinstance(field.struct_type, schema.Struct):
        raise exception.InvalidType(
            'Message {}, referenced by field {}, is not a struct. '
            'Fields may only reference structs.'.format(
                field.struct_type.full_name, field.full_name))
      # set metadata for legacy support
      field.metadata = field.struct_type
    elif field.data_type is schema.Field.DataType.ENUM:
      field.enum_type = self.get_obj(field_desc.type_name)
      # set metadata for legacy support
      field.metadata = field.enum_type

    self.parse_options(field, options)