Home | History | Annotate | Download | only in protobuf
      1 # Protocol Buffers - Google's data interchange format
      2 # Copyright 2008 Google Inc.  All rights reserved.
      3 # http://code.google.com/p/protobuf/
      4 #
      5 # Redistribution and use in source and binary forms, with or without
      6 # modification, are permitted provided that the following conditions are
      7 # met:
      8 #
      9 #     * Redistributions of source code must retain the above copyright
     10 # notice, this list of conditions and the following disclaimer.
     11 #     * Redistributions in binary form must reproduce the above
     12 # copyright notice, this list of conditions and the following disclaimer
     13 # in the documentation and/or other materials provided with the
     14 # distribution.
     15 #     * Neither the name of Google Inc. nor the names of its
     16 # contributors may be used to endorse or promote products derived from
     17 # this software without specific prior written permission.
     18 #
     19 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
     20 # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
     21 # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
     22 # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
     23 # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
     24 # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
     25 # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
     26 # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
     27 # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     28 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
     29 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     30 
     31 # TODO(robinson): We probably need to provide deep-copy methods for
     32 # descriptor types.  When a FieldDescriptor is passed into
     33 # Descriptor.__init__(), we should make a deep copy and then set
     34 # containing_type on it.  Alternatively, we could just get
     35 # rid of containing_type (iit's not needed for reflection.py, at least).
     36 #
     37 # TODO(robinson): Print method?
     38 #
     39 # TODO(robinson): Useful __repr__?
     40 
     41 """Descriptors essentially contain exactly the information found in a .proto
     42 file, in types that make this information accessible in Python.
     43 """
     44 
     45 __author__ = 'robinson (at] google.com (Will Robinson)'
     46 
     47 
     48 class Error(Exception):
     49   """Base error for this module."""
     50 
     51 
     52 class DescriptorBase(object):
     53 
     54   """Descriptors base class.
     55 
     56   This class is the base of all descriptor classes. It provides common options
     57   related functionaility.
     58 
     59   Attributes:
     60     has_options:  True if the descriptor has non-default options.  Usually it
     61         is not necessary to read this -- just call GetOptions() which will
     62         happily return the default instance.  However, it's sometimes useful
     63         for efficiency, and also useful inside the protobuf implementation to
     64         avoid some bootstrapping issues.
     65   """
     66 
     67   def __init__(self, options, options_class_name):
     68     """Initialize the descriptor given its options message and the name of the
     69     class of the options message. The name of the class is required in case
     70     the options message is None and has to be created.
     71     """
     72     self._options = options
     73     self._options_class_name = options_class_name
     74 
     75     # Does this descriptor have non-default options?
     76     self.has_options = options is not None
     77 
     78   def GetOptions(self):
     79     """Retrieves descriptor options.
     80 
     81     This method returns the options set or creates the default options for the
     82     descriptor.
     83     """
     84     if self._options:
     85       return self._options
     86     from google.protobuf import descriptor_pb2
     87     try:
     88       options_class = getattr(descriptor_pb2, self._options_class_name)
     89     except AttributeError:
     90       raise RuntimeError('Unknown options class name %s!' %
     91                          (self._options_class_name))
     92     self._options = options_class()
     93     return self._options
     94 
     95 
     96 class _NestedDescriptorBase(DescriptorBase):
     97   """Common class for descriptors that can be nested."""
     98 
     99   def __init__(self, options, options_class_name, name, full_name,
    100                file, containing_type, serialized_start=None,
    101                serialized_end=None):
    102     """Constructor.
    103 
    104     Args:
    105       options: Protocol message options or None
    106         to use default message options.
    107       options_class_name: (str) The class name of the above options.
    108 
    109       name: (str) Name of this protocol message type.
    110       full_name: (str) Fully-qualified name of this protocol message type,
    111         which will include protocol "package" name and the name of any
    112         enclosing types.
    113       file: (FileDescriptor) Reference to file info.
    114       containing_type: if provided, this is a nested descriptor, with this
    115         descriptor as parent, otherwise None.
    116       serialized_start: The start index (inclusive) in block in the
    117         file.serialized_pb that describes this descriptor.
    118       serialized_end: The end index (exclusive) in block in the
    119         file.serialized_pb that describes this descriptor.
    120     """
    121     super(_NestedDescriptorBase, self).__init__(
    122         options, options_class_name)
    123 
    124     self.name = name
    125     # TODO(falk): Add function to calculate full_name instead of having it in
    126     #             memory?
    127     self.full_name = full_name
    128     self.file = file
    129     self.containing_type = containing_type
    130 
    131     self._serialized_start = serialized_start
    132     self._serialized_end = serialized_end
    133 
    134   def GetTopLevelContainingType(self):
    135     """Returns the root if this is a nested type, or itself if its the root."""
    136     desc = self
    137     while desc.containing_type is not None:
    138       desc = desc.containing_type
    139     return desc
    140 
    141   def CopyToProto(self, proto):
    142     """Copies this to the matching proto in descriptor_pb2.
    143 
    144     Args:
    145       proto: An empty proto instance from descriptor_pb2.
    146 
    147     Raises:
    148       Error: If self couldnt be serialized, due to to few constructor arguments.
    149     """
    150     if (self.file is not None and
    151         self._serialized_start is not None and
    152         self._serialized_end is not None):
    153       proto.ParseFromString(self.file.serialized_pb[
    154           self._serialized_start:self._serialized_end])
    155     else:
    156       raise Error('Descriptor does not contain serialization.')
    157 
    158 
    159 class Descriptor(_NestedDescriptorBase):
    160 
    161   """Descriptor for a protocol message type.
    162 
    163   A Descriptor instance has the following attributes:
    164 
    165     name: (str) Name of this protocol message type.
    166     full_name: (str) Fully-qualified name of this protocol message type,
    167       which will include protocol "package" name and the name of any
    168       enclosing types.
    169 
    170     containing_type: (Descriptor) Reference to the descriptor of the
    171       type containing us, or None if this is top-level.
    172 
    173     fields: (list of FieldDescriptors) Field descriptors for all
    174       fields in this type.
    175     fields_by_number: (dict int -> FieldDescriptor) Same FieldDescriptor
    176       objects as in |fields|, but indexed by "number" attribute in each
    177       FieldDescriptor.
    178     fields_by_name: (dict str -> FieldDescriptor) Same FieldDescriptor
    179       objects as in |fields|, but indexed by "name" attribute in each
    180       FieldDescriptor.
    181 
    182     nested_types: (list of Descriptors) Descriptor references
    183       for all protocol message types nested within this one.
    184     nested_types_by_name: (dict str -> Descriptor) Same Descriptor
    185       objects as in |nested_types|, but indexed by "name" attribute
    186       in each Descriptor.
    187 
    188     enum_types: (list of EnumDescriptors) EnumDescriptor references
    189       for all enums contained within this type.
    190     enum_types_by_name: (dict str ->EnumDescriptor) Same EnumDescriptor
    191       objects as in |enum_types|, but indexed by "name" attribute
    192       in each EnumDescriptor.
    193     enum_values_by_name: (dict str -> EnumValueDescriptor) Dict mapping
    194       from enum value name to EnumValueDescriptor for that value.
    195 
    196     extensions: (list of FieldDescriptor) All extensions defined directly
    197       within this message type (NOT within a nested type).
    198     extensions_by_name: (dict, string -> FieldDescriptor) Same FieldDescriptor
    199       objects as |extensions|, but indexed by "name" attribute of each
    200       FieldDescriptor.
    201 
    202     is_extendable:  Does this type define any extension ranges?
    203 
    204     options: (descriptor_pb2.MessageOptions) Protocol message options or None
    205       to use default message options.
    206 
    207     file: (FileDescriptor) Reference to file descriptor.
    208   """
    209 
    210   def __init__(self, name, full_name, filename, containing_type, fields,
    211                nested_types, enum_types, extensions, options=None,
    212                is_extendable=True, extension_ranges=None, file=None,
    213                serialized_start=None, serialized_end=None):
    214     """Arguments to __init__() are as described in the description
    215     of Descriptor fields above.
    216 
    217     Note that filename is an obsolete argument, that is not used anymore.
    218     Please use file.name to access this as an attribute.
    219     """
    220     super(Descriptor, self).__init__(
    221         options, 'MessageOptions', name, full_name, file,
    222         containing_type, serialized_start=serialized_start,
    223         serialized_end=serialized_start)
    224 
    225     # We have fields in addition to fields_by_name and fields_by_number,
    226     # so that:
    227     #   1. Clients can index fields by "order in which they're listed."
    228     #   2. Clients can easily iterate over all fields with the terse
    229     #      syntax: for f in descriptor.fields: ...
    230     self.fields = fields
    231     for field in self.fields:
    232       field.containing_type = self
    233     self.fields_by_number = dict((f.number, f) for f in fields)
    234     self.fields_by_name = dict((f.name, f) for f in fields)
    235 
    236     self.nested_types = nested_types
    237     self.nested_types_by_name = dict((t.name, t) for t in nested_types)
    238 
    239     self.enum_types = enum_types
    240     for enum_type in self.enum_types:
    241       enum_type.containing_type = self
    242     self.enum_types_by_name = dict((t.name, t) for t in enum_types)
    243     self.enum_values_by_name = dict(
    244         (v.name, v) for t in enum_types for v in t.values)
    245 
    246     self.extensions = extensions
    247     for extension in self.extensions:
    248       extension.extension_scope = self
    249     self.extensions_by_name = dict((f.name, f) for f in extensions)
    250     self.is_extendable = is_extendable
    251     self.extension_ranges = extension_ranges
    252 
    253     self._serialized_start = serialized_start
    254     self._serialized_end = serialized_end
    255 
    256   def CopyToProto(self, proto):
    257     """Copies this to a descriptor_pb2.DescriptorProto.
    258 
    259     Args:
    260       proto: An empty descriptor_pb2.DescriptorProto.
    261     """
    262     # This function is overriden to give a better doc comment.
    263     super(Descriptor, self).CopyToProto(proto)
    264 
    265 
    266 # TODO(robinson): We should have aggressive checking here,
    267 # for example:
    268 #   * If you specify a repeated field, you should not be allowed
    269 #     to specify a default value.
    270 #   * [Other examples here as needed].
    271 #
    272 # TODO(robinson): for this and other *Descriptor classes, we
    273 # might also want to lock things down aggressively (e.g.,
    274 # prevent clients from setting the attributes).  Having
    275 # stronger invariants here in general will reduce the number
    276 # of runtime checks we must do in reflection.py...
    277 class FieldDescriptor(DescriptorBase):
    278 
    279   """Descriptor for a single field in a .proto file.
    280 
    281   A FieldDescriptor instance has the following attriubtes:
    282 
    283     name: (str) Name of this field, exactly as it appears in .proto.
    284     full_name: (str) Name of this field, including containing scope.  This is
    285       particularly relevant for extensions.
    286     index: (int) Dense, 0-indexed index giving the order that this
    287       field textually appears within its message in the .proto file.
    288     number: (int) Tag number declared for this field in the .proto file.
    289 
    290     type: (One of the TYPE_* constants below) Declared type.
    291     cpp_type: (One of the CPPTYPE_* constants below) C++ type used to
    292       represent this field.
    293 
    294     label: (One of the LABEL_* constants below) Tells whether this
    295       field is optional, required, or repeated.
    296     has_default_value: (bool) True if this field has a default value defined,
    297       otherwise false.
    298     default_value: (Varies) Default value of this field.  Only
    299       meaningful for non-repeated scalar fields.  Repeated fields
    300       should always set this to [], and non-repeated composite
    301       fields should always set this to None.
    302 
    303     containing_type: (Descriptor) Descriptor of the protocol message
    304       type that contains this field.  Set by the Descriptor constructor
    305       if we're passed into one.
    306       Somewhat confusingly, for extension fields, this is the
    307       descriptor of the EXTENDED message, not the descriptor
    308       of the message containing this field.  (See is_extension and
    309       extension_scope below).
    310     message_type: (Descriptor) If a composite field, a descriptor
    311       of the message type contained in this field.  Otherwise, this is None.
    312     enum_type: (EnumDescriptor) If this field contains an enum, a
    313       descriptor of that enum.  Otherwise, this is None.
    314 
    315     is_extension: True iff this describes an extension field.
    316     extension_scope: (Descriptor) Only meaningful if is_extension is True.
    317       Gives the message that immediately contains this extension field.
    318       Will be None iff we're a top-level (file-level) extension field.
    319 
    320     options: (descriptor_pb2.FieldOptions) Protocol message field options or
    321       None to use default field options.
    322   """
    323 
    324   # Must be consistent with C++ FieldDescriptor::Type enum in
    325   # descriptor.h.
    326   #
    327   # TODO(robinson): Find a way to eliminate this repetition.
    328   TYPE_DOUBLE         = 1
    329   TYPE_FLOAT          = 2
    330   TYPE_INT64          = 3
    331   TYPE_UINT64         = 4
    332   TYPE_INT32          = 5
    333   TYPE_FIXED64        = 6
    334   TYPE_FIXED32        = 7
    335   TYPE_BOOL           = 8
    336   TYPE_STRING         = 9
    337   TYPE_GROUP          = 10
    338   TYPE_MESSAGE        = 11
    339   TYPE_BYTES          = 12
    340   TYPE_UINT32         = 13
    341   TYPE_ENUM           = 14
    342   TYPE_SFIXED32       = 15
    343   TYPE_SFIXED64       = 16
    344   TYPE_SINT32         = 17
    345   TYPE_SINT64         = 18
    346   MAX_TYPE            = 18
    347 
    348   # Must be consistent with C++ FieldDescriptor::CppType enum in
    349   # descriptor.h.
    350   #
    351   # TODO(robinson): Find a way to eliminate this repetition.
    352   CPPTYPE_INT32       = 1
    353   CPPTYPE_INT64       = 2
    354   CPPTYPE_UINT32      = 3
    355   CPPTYPE_UINT64      = 4
    356   CPPTYPE_DOUBLE      = 5
    357   CPPTYPE_FLOAT       = 6
    358   CPPTYPE_BOOL        = 7
    359   CPPTYPE_ENUM        = 8
    360   CPPTYPE_STRING      = 9
    361   CPPTYPE_MESSAGE     = 10
    362   MAX_CPPTYPE         = 10
    363 
    364   # Must be consistent with C++ FieldDescriptor::Label enum in
    365   # descriptor.h.
    366   #
    367   # TODO(robinson): Find a way to eliminate this repetition.
    368   LABEL_OPTIONAL      = 1
    369   LABEL_REQUIRED      = 2
    370   LABEL_REPEATED      = 3
    371   MAX_LABEL           = 3
    372 
    373   def __init__(self, name, full_name, index, number, type, cpp_type, label,
    374                default_value, message_type, enum_type, containing_type,
    375                is_extension, extension_scope, options=None,
    376                has_default_value=True):
    377     """The arguments are as described in the description of FieldDescriptor
    378     attributes above.
    379 
    380     Note that containing_type may be None, and may be set later if necessary
    381     (to deal with circular references between message types, for example).
    382     Likewise for extension_scope.
    383     """
    384     super(FieldDescriptor, self).__init__(options, 'FieldOptions')
    385     self.name = name
    386     self.full_name = full_name
    387     self.index = index
    388     self.number = number
    389     self.type = type
    390     self.cpp_type = cpp_type
    391     self.label = label
    392     self.has_default_value = has_default_value
    393     self.default_value = default_value
    394     self.containing_type = containing_type
    395     self.message_type = message_type
    396     self.enum_type = enum_type
    397     self.is_extension = is_extension
    398     self.extension_scope = extension_scope
    399 
    400 
    401 class EnumDescriptor(_NestedDescriptorBase):
    402 
    403   """Descriptor for an enum defined in a .proto file.
    404 
    405   An EnumDescriptor instance has the following attributes:
    406 
    407     name: (str) Name of the enum type.
    408     full_name: (str) Full name of the type, including package name
    409       and any enclosing type(s).
    410 
    411     values: (list of EnumValueDescriptors) List of the values
    412       in this enum.
    413     values_by_name: (dict str -> EnumValueDescriptor) Same as |values|,
    414       but indexed by the "name" field of each EnumValueDescriptor.
    415     values_by_number: (dict int -> EnumValueDescriptor) Same as |values|,
    416       but indexed by the "number" field of each EnumValueDescriptor.
    417     containing_type: (Descriptor) Descriptor of the immediate containing
    418       type of this enum, or None if this is an enum defined at the
    419       top level in a .proto file.  Set by Descriptor's constructor
    420       if we're passed into one.
    421     file: (FileDescriptor) Reference to file descriptor.
    422     options: (descriptor_pb2.EnumOptions) Enum options message or
    423       None to use default enum options.
    424   """
    425 
    426   def __init__(self, name, full_name, filename, values,
    427                containing_type=None, options=None, file=None,
    428                serialized_start=None, serialized_end=None):
    429     """Arguments are as described in the attribute description above.
    430 
    431     Note that filename is an obsolete argument, that is not used anymore.
    432     Please use file.name to access this as an attribute.
    433     """
    434     super(EnumDescriptor, self).__init__(
    435         options, 'EnumOptions', name, full_name, file,
    436         containing_type, serialized_start=serialized_start,
    437         serialized_end=serialized_start)
    438 
    439     self.values = values
    440     for value in self.values:
    441       value.type = self
    442     self.values_by_name = dict((v.name, v) for v in values)
    443     self.values_by_number = dict((v.number, v) for v in values)
    444 
    445     self._serialized_start = serialized_start
    446     self._serialized_end = serialized_end
    447 
    448   def CopyToProto(self, proto):
    449     """Copies this to a descriptor_pb2.EnumDescriptorProto.
    450 
    451     Args:
    452       proto: An empty descriptor_pb2.EnumDescriptorProto.
    453     """
    454     # This function is overriden to give a better doc comment.
    455     super(EnumDescriptor, self).CopyToProto(proto)
    456 
    457 
    458 class EnumValueDescriptor(DescriptorBase):
    459 
    460   """Descriptor for a single value within an enum.
    461 
    462     name: (str) Name of this value.
    463     index: (int) Dense, 0-indexed index giving the order that this
    464       value appears textually within its enum in the .proto file.
    465     number: (int) Actual number assigned to this enum value.
    466     type: (EnumDescriptor) EnumDescriptor to which this value
    467       belongs.  Set by EnumDescriptor's constructor if we're
    468       passed into one.
    469     options: (descriptor_pb2.EnumValueOptions) Enum value options message or
    470       None to use default enum value options options.
    471   """
    472 
    473   def __init__(self, name, index, number, type=None, options=None):
    474     """Arguments are as described in the attribute description above."""
    475     super(EnumValueDescriptor, self).__init__(options, 'EnumValueOptions')
    476     self.name = name
    477     self.index = index
    478     self.number = number
    479     self.type = type
    480 
    481 
    482 class ServiceDescriptor(_NestedDescriptorBase):
    483 
    484   """Descriptor for a service.
    485 
    486     name: (str) Name of the service.
    487     full_name: (str) Full name of the service, including package name.
    488     index: (int) 0-indexed index giving the order that this services
    489       definition appears withing the .proto file.
    490     methods: (list of MethodDescriptor) List of methods provided by this
    491       service.
    492     options: (descriptor_pb2.ServiceOptions) Service options message or
    493       None to use default service options.
    494     file: (FileDescriptor) Reference to file info.
    495   """
    496 
    497   def __init__(self, name, full_name, index, methods, options=None, file=None,
    498                serialized_start=None, serialized_end=None):
    499     super(ServiceDescriptor, self).__init__(
    500         options, 'ServiceOptions', name, full_name, file,
    501         None, serialized_start=serialized_start,
    502         serialized_end=serialized_end)
    503     self.index = index
    504     self.methods = methods
    505     # Set the containing service for each method in this service.
    506     for method in self.methods:
    507       method.containing_service = self
    508 
    509   def FindMethodByName(self, name):
    510     """Searches for the specified method, and returns its descriptor."""
    511     for method in self.methods:
    512       if name == method.name:
    513         return method
    514     return None
    515 
    516   def CopyToProto(self, proto):
    517     """Copies this to a descriptor_pb2.ServiceDescriptorProto.
    518 
    519     Args:
    520       proto: An empty descriptor_pb2.ServiceDescriptorProto.
    521     """
    522     # This function is overriden to give a better doc comment.
    523     super(ServiceDescriptor, self).CopyToProto(proto)
    524 
    525 
    526 class MethodDescriptor(DescriptorBase):
    527 
    528   """Descriptor for a method in a service.
    529 
    530   name: (str) Name of the method within the service.
    531   full_name: (str) Full name of method.
    532   index: (int) 0-indexed index of the method inside the service.
    533   containing_service: (ServiceDescriptor) The service that contains this
    534     method.
    535   input_type: The descriptor of the message that this method accepts.
    536   output_type: The descriptor of the message that this method returns.
    537   options: (descriptor_pb2.MethodOptions) Method options message or
    538     None to use default method options.
    539   """
    540 
    541   def __init__(self, name, full_name, index, containing_service,
    542                input_type, output_type, options=None):
    543     """The arguments are as described in the description of MethodDescriptor
    544     attributes above.
    545 
    546     Note that containing_service may be None, and may be set later if necessary.
    547     """
    548     super(MethodDescriptor, self).__init__(options, 'MethodOptions')
    549     self.name = name
    550     self.full_name = full_name
    551     self.index = index
    552     self.containing_service = containing_service
    553     self.input_type = input_type
    554     self.output_type = output_type
    555 
    556 
    557 class FileDescriptor(DescriptorBase):
    558   """Descriptor for a file. Mimics the descriptor_pb2.FileDescriptorProto.
    559 
    560   name: name of file, relative to root of source tree.
    561   package: name of the package
    562   serialized_pb: (str) Byte string of serialized
    563     descriptor_pb2.FileDescriptorProto.
    564   """
    565 
    566   def __init__(self, name, package, options=None, serialized_pb=None):
    567     """Constructor."""
    568     super(FileDescriptor, self).__init__(options, 'FileOptions')
    569 
    570     self.name = name
    571     self.package = package
    572     self.serialized_pb = serialized_pb
    573 
    574   def CopyToProto(self, proto):
    575     """Copies this to a descriptor_pb2.FileDescriptorProto.
    576 
    577     Args:
    578       proto: An empty descriptor_pb2.FileDescriptorProto.
    579     """
    580     proto.ParseFromString(self.serialized_pb)
    581 
    582 
    583 def _ParseOptions(message, string):
    584   """Parses serialized options.
    585 
    586   This helper function is used to parse serialized options in generated
    587   proto2 files. It must not be used outside proto2.
    588   """
    589   message.ParseFromString(string)
    590   return message
    591