1# Protocol Buffers - Google's data interchange format
2# Copyright 2008 Google Inc.  All rights reserved.
3# https://developers.google.com/protocol-buffers/
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# Needs to stay compatible with Python 2.5 due to GAE.
32#
33# Copyright 2007 Google Inc. All Rights Reserved.
34
35"""Descriptors essentially contain exactly the information found in a .proto
36file, in types that make this information accessible in Python.
37"""
38
39__author__ = 'robinson@google.com (Will Robinson)'
40
41from google.protobuf.internal import api_implementation
42
43
44if api_implementation.Type() == 'cpp':
45  # Used by MakeDescriptor in cpp mode
46  import os
47  import uuid
48
49  if api_implementation.Version() == 2:
50    from google.protobuf.pyext import _message
51  else:
52    from google.protobuf.internal import cpp_message
53
54
55class Error(Exception):
56  """Base error for this module."""
57
58
59class TypeTransformationError(Error):
60  """Error transforming between python proto type and corresponding C++ type."""
61
62
63class DescriptorBase(object):
64
65  """Descriptors base class.
66
67  This class is the base of all descriptor classes. It provides common options
68  related functionaility.
69
70  Attributes:
71    has_options:  True if the descriptor has non-default options.  Usually it
72        is not necessary to read this -- just call GetOptions() which will
73        happily return the default instance.  However, it's sometimes useful
74        for efficiency, and also useful inside the protobuf implementation to
75        avoid some bootstrapping issues.
76  """
77
78  def __init__(self, options, options_class_name):
79    """Initialize the descriptor given its options message and the name of the
80    class of the options message. The name of the class is required in case
81    the options message is None and has to be created.
82    """
83    self._options = options
84    self._options_class_name = options_class_name
85
86    # Does this descriptor have non-default options?
87    self.has_options = options is not None
88
89  def _SetOptions(self, options, options_class_name):
90    """Sets the descriptor's options
91
92    This function is used in generated proto2 files to update descriptor
93    options. It must not be used outside proto2.
94    """
95    self._options = options
96    self._options_class_name = options_class_name
97
98    # Does this descriptor have non-default options?
99    self.has_options = options is not None
100
101  def GetOptions(self):
102    """Retrieves descriptor options.
103
104    This method returns the options set or creates the default options for the
105    descriptor.
106    """
107    if self._options:
108      return self._options
109    from google.protobuf import descriptor_pb2
110    try:
111      options_class = getattr(descriptor_pb2, self._options_class_name)
112    except AttributeError:
113      raise RuntimeError('Unknown options class name %s!' %
114                         (self._options_class_name))
115    self._options = options_class()
116    return self._options
117
118
119class _NestedDescriptorBase(DescriptorBase):
120  """Common class for descriptors that can be nested."""
121
122  def __init__(self, options, options_class_name, name, full_name,
123               file, containing_type, serialized_start=None,
124               serialized_end=None):
125    """Constructor.
126
127    Args:
128      options: Protocol message options or None
129        to use default message options.
130      options_class_name: (str) The class name of the above options.
131
132      name: (str) Name of this protocol message type.
133      full_name: (str) Fully-qualified name of this protocol message type,
134        which will include protocol "package" name and the name of any
135        enclosing types.
136      file: (FileDescriptor) Reference to file info.
137      containing_type: if provided, this is a nested descriptor, with this
138        descriptor as parent, otherwise None.
139      serialized_start: The start index (inclusive) in block in the
140        file.serialized_pb that describes this descriptor.
141      serialized_end: The end index (exclusive) in block in the
142        file.serialized_pb that describes this descriptor.
143    """
144    super(_NestedDescriptorBase, self).__init__(
145        options, options_class_name)
146
147    self.name = name
148    # TODO(falk): Add function to calculate full_name instead of having it in
149    #             memory?
150    self.full_name = full_name
151    self.file = file
152    self.containing_type = containing_type
153
154    self._serialized_start = serialized_start
155    self._serialized_end = serialized_end
156
157  def GetTopLevelContainingType(self):
158    """Returns the root if this is a nested type, or itself if its the root."""
159    desc = self
160    while desc.containing_type is not None:
161      desc = desc.containing_type
162    return desc
163
164  def CopyToProto(self, proto):
165    """Copies this to the matching proto in descriptor_pb2.
166
167    Args:
168      proto: An empty proto instance from descriptor_pb2.
169
170    Raises:
171      Error: If self couldnt be serialized, due to to few constructor arguments.
172    """
173    if (self.file is not None and
174        self._serialized_start is not None and
175        self._serialized_end is not None):
176      proto.ParseFromString(self.file.serialized_pb[
177          self._serialized_start:self._serialized_end])
178    else:
179      raise Error('Descriptor does not contain serialization.')
180
181
182class Descriptor(_NestedDescriptorBase):
183
184  """Descriptor for a protocol message type.
185
186  A Descriptor instance has the following attributes:
187
188    name: (str) Name of this protocol message type.
189    full_name: (str) Fully-qualified name of this protocol message type,
190      which will include protocol "package" name and the name of any
191      enclosing types.
192
193    containing_type: (Descriptor) Reference to the descriptor of the
194      type containing us, or None if this is top-level.
195
196    fields: (list of FieldDescriptors) Field descriptors for all
197      fields in this type.
198    fields_by_number: (dict int -> FieldDescriptor) Same FieldDescriptor
199      objects as in |fields|, but indexed by "number" attribute in each
200      FieldDescriptor.
201    fields_by_name: (dict str -> FieldDescriptor) Same FieldDescriptor
202      objects as in |fields|, but indexed by "name" attribute in each
203      FieldDescriptor.
204
205    nested_types: (list of Descriptors) Descriptor references
206      for all protocol message types nested within this one.
207    nested_types_by_name: (dict str -> Descriptor) Same Descriptor
208      objects as in |nested_types|, but indexed by "name" attribute
209      in each Descriptor.
210
211    enum_types: (list of EnumDescriptors) EnumDescriptor references
212      for all enums contained within this type.
213    enum_types_by_name: (dict str ->EnumDescriptor) Same EnumDescriptor
214      objects as in |enum_types|, but indexed by "name" attribute
215      in each EnumDescriptor.
216    enum_values_by_name: (dict str -> EnumValueDescriptor) Dict mapping
217      from enum value name to EnumValueDescriptor for that value.
218
219    extensions: (list of FieldDescriptor) All extensions defined directly
220      within this message type (NOT within a nested type).
221    extensions_by_name: (dict, string -> FieldDescriptor) Same FieldDescriptor
222      objects as |extensions|, but indexed by "name" attribute of each
223      FieldDescriptor.
224
225    is_extendable:  Does this type define any extension ranges?
226
227    options: (descriptor_pb2.MessageOptions) Protocol message options or None
228      to use default message options.
229
230    oneofs: (list of OneofDescriptor) The list of descriptors for oneof fields
231      in this message.
232    oneofs_by_name: (dict str -> OneofDescriptor) Same objects as in |oneofs|,
233      but indexed by "name" attribute.
234
235    file: (FileDescriptor) Reference to file descriptor.
236  """
237
238  # NOTE(tmarek): The file argument redefining a builtin is nothing we can
239  # fix right now since we don't know how many clients already rely on the
240  # name of the argument.
241  def __init__(self, name, full_name, filename, containing_type, fields,
242               nested_types, enum_types, extensions, options=None,
243               is_extendable=True, extension_ranges=None, oneofs=None,
244               file=None, serialized_start=None, serialized_end=None):  # pylint:disable=redefined-builtin
245    """Arguments to __init__() are as described in the description
246    of Descriptor fields above.
247
248    Note that filename is an obsolete argument, that is not used anymore.
249    Please use file.name to access this as an attribute.
250    """
251    super(Descriptor, self).__init__(
252        options, 'MessageOptions', name, full_name, file,
253        containing_type, serialized_start=serialized_start,
254        serialized_end=serialized_end)
255
256    # We have fields in addition to fields_by_name and fields_by_number,
257    # so that:
258    #   1. Clients can index fields by "order in which they're listed."
259    #   2. Clients can easily iterate over all fields with the terse
260    #      syntax: for f in descriptor.fields: ...
261    self.fields = fields
262    for field in self.fields:
263      field.containing_type = self
264    self.fields_by_number = dict((f.number, f) for f in fields)
265    self.fields_by_name = dict((f.name, f) for f in fields)
266
267    self.nested_types = nested_types
268    for nested_type in nested_types:
269      nested_type.containing_type = self
270    self.nested_types_by_name = dict((t.name, t) for t in nested_types)
271
272    self.enum_types = enum_types
273    for enum_type in self.enum_types:
274      enum_type.containing_type = self
275    self.enum_types_by_name = dict((t.name, t) for t in enum_types)
276    self.enum_values_by_name = dict(
277        (v.name, v) for t in enum_types for v in t.values)
278
279    self.extensions = extensions
280    for extension in self.extensions:
281      extension.extension_scope = self
282    self.extensions_by_name = dict((f.name, f) for f in extensions)
283    self.is_extendable = is_extendable
284    self.extension_ranges = extension_ranges
285    self.oneofs = oneofs if oneofs is not None else []
286    self.oneofs_by_name = dict((o.name, o) for o in self.oneofs)
287    for oneof in self.oneofs:
288      oneof.containing_type = self
289
290  def EnumValueName(self, enum, value):
291    """Returns the string name of an enum value.
292
293    This is just a small helper method to simplify a common operation.
294
295    Args:
296      enum: string name of the Enum.
297      value: int, value of the enum.
298
299    Returns:
300      string name of the enum value.
301
302    Raises:
303      KeyError if either the Enum doesn't exist or the value is not a valid
304        value for the enum.
305    """
306    return self.enum_types_by_name[enum].values_by_number[value].name
307
308  def CopyToProto(self, proto):
309    """Copies this to a descriptor_pb2.DescriptorProto.
310
311    Args:
312      proto: An empty descriptor_pb2.DescriptorProto.
313    """
314    # This function is overriden to give a better doc comment.
315    super(Descriptor, self).CopyToProto(proto)
316
317
318# TODO(robinson): We should have aggressive checking here,
319# for example:
320#   * If you specify a repeated field, you should not be allowed
321#     to specify a default value.
322#   * [Other examples here as needed].
323#
324# TODO(robinson): for this and other *Descriptor classes, we
325# might also want to lock things down aggressively (e.g.,
326# prevent clients from setting the attributes).  Having
327# stronger invariants here in general will reduce the number
328# of runtime checks we must do in reflection.py...
329class FieldDescriptor(DescriptorBase):
330
331  """Descriptor for a single field in a .proto file.
332
333  A FieldDescriptor instance has the following attributes:
334
335    name: (str) Name of this field, exactly as it appears in .proto.
336    full_name: (str) Name of this field, including containing scope.  This is
337      particularly relevant for extensions.
338    index: (int) Dense, 0-indexed index giving the order that this
339      field textually appears within its message in the .proto file.
340    number: (int) Tag number declared for this field in the .proto file.
341
342    type: (One of the TYPE_* constants below) Declared type.
343    cpp_type: (One of the CPPTYPE_* constants below) C++ type used to
344      represent this field.
345
346    label: (One of the LABEL_* constants below) Tells whether this
347      field is optional, required, or repeated.
348    has_default_value: (bool) True if this field has a default value defined,
349      otherwise false.
350    default_value: (Varies) Default value of this field.  Only
351      meaningful for non-repeated scalar fields.  Repeated fields
352      should always set this to [], and non-repeated composite
353      fields should always set this to None.
354
355    containing_type: (Descriptor) Descriptor of the protocol message
356      type that contains this field.  Set by the Descriptor constructor
357      if we're passed into one.
358      Somewhat confusingly, for extension fields, this is the
359      descriptor of the EXTENDED message, not the descriptor
360      of the message containing this field.  (See is_extension and
361      extension_scope below).
362    message_type: (Descriptor) If a composite field, a descriptor
363      of the message type contained in this field.  Otherwise, this is None.
364    enum_type: (EnumDescriptor) If this field contains an enum, a
365      descriptor of that enum.  Otherwise, this is None.
366
367    is_extension: True iff this describes an extension field.
368    extension_scope: (Descriptor) Only meaningful if is_extension is True.
369      Gives the message that immediately contains this extension field.
370      Will be None iff we're a top-level (file-level) extension field.
371
372    options: (descriptor_pb2.FieldOptions) Protocol message field options or
373      None to use default field options.
374
375    containing_oneof: (OneofDescriptor) If the field is a member of a oneof
376      union, contains its descriptor. Otherwise, None.
377  """
378
379  # Must be consistent with C++ FieldDescriptor::Type enum in
380  # descriptor.h.
381  #
382  # TODO(robinson): Find a way to eliminate this repetition.
383  TYPE_DOUBLE         = 1
384  TYPE_FLOAT          = 2
385  TYPE_INT64          = 3
386  TYPE_UINT64         = 4
387  TYPE_INT32          = 5
388  TYPE_FIXED64        = 6
389  TYPE_FIXED32        = 7
390  TYPE_BOOL           = 8
391  TYPE_STRING         = 9
392  TYPE_GROUP          = 10
393  TYPE_MESSAGE        = 11
394  TYPE_BYTES          = 12
395  TYPE_UINT32         = 13
396  TYPE_ENUM           = 14
397  TYPE_SFIXED32       = 15
398  TYPE_SFIXED64       = 16
399  TYPE_SINT32         = 17
400  TYPE_SINT64         = 18
401  MAX_TYPE            = 18
402
403  # Must be consistent with C++ FieldDescriptor::CppType enum in
404  # descriptor.h.
405  #
406  # TODO(robinson): Find a way to eliminate this repetition.
407  CPPTYPE_INT32       = 1
408  CPPTYPE_INT64       = 2
409  CPPTYPE_UINT32      = 3
410  CPPTYPE_UINT64      = 4
411  CPPTYPE_DOUBLE      = 5
412  CPPTYPE_FLOAT       = 6
413  CPPTYPE_BOOL        = 7
414  CPPTYPE_ENUM        = 8
415  CPPTYPE_STRING      = 9
416  CPPTYPE_MESSAGE     = 10
417  MAX_CPPTYPE         = 10
418
419  _PYTHON_TO_CPP_PROTO_TYPE_MAP = {
420      TYPE_DOUBLE: CPPTYPE_DOUBLE,
421      TYPE_FLOAT: CPPTYPE_FLOAT,
422      TYPE_ENUM: CPPTYPE_ENUM,
423      TYPE_INT64: CPPTYPE_INT64,
424      TYPE_SINT64: CPPTYPE_INT64,
425      TYPE_SFIXED64: CPPTYPE_INT64,
426      TYPE_UINT64: CPPTYPE_UINT64,
427      TYPE_FIXED64: CPPTYPE_UINT64,
428      TYPE_INT32: CPPTYPE_INT32,
429      TYPE_SFIXED32: CPPTYPE_INT32,
430      TYPE_SINT32: CPPTYPE_INT32,
431      TYPE_UINT32: CPPTYPE_UINT32,
432      TYPE_FIXED32: CPPTYPE_UINT32,
433      TYPE_BYTES: CPPTYPE_STRING,
434      TYPE_STRING: CPPTYPE_STRING,
435      TYPE_BOOL: CPPTYPE_BOOL,
436      TYPE_MESSAGE: CPPTYPE_MESSAGE,
437      TYPE_GROUP: CPPTYPE_MESSAGE
438      }
439
440  # Must be consistent with C++ FieldDescriptor::Label enum in
441  # descriptor.h.
442  #
443  # TODO(robinson): Find a way to eliminate this repetition.
444  LABEL_OPTIONAL      = 1
445  LABEL_REQUIRED      = 2
446  LABEL_REPEATED      = 3
447  MAX_LABEL           = 3
448
449  # Must be consistent with C++ constants kMaxNumber, kFirstReservedNumber,
450  # and kLastReservedNumber in descriptor.h
451  MAX_FIELD_NUMBER = (1 << 29) - 1
452  FIRST_RESERVED_FIELD_NUMBER = 19000
453  LAST_RESERVED_FIELD_NUMBER = 19999
454
455  def __init__(self, name, full_name, index, number, type, cpp_type, label,
456               default_value, message_type, enum_type, containing_type,
457               is_extension, extension_scope, options=None,
458               has_default_value=True, containing_oneof=None):
459    """The arguments are as described in the description of FieldDescriptor
460    attributes above.
461
462    Note that containing_type may be None, and may be set later if necessary
463    (to deal with circular references between message types, for example).
464    Likewise for extension_scope.
465    """
466    super(FieldDescriptor, self).__init__(options, 'FieldOptions')
467    self.name = name
468    self.full_name = full_name
469    self.index = index
470    self.number = number
471    self.type = type
472    self.cpp_type = cpp_type
473    self.label = label
474    self.has_default_value = has_default_value
475    self.default_value = default_value
476    self.containing_type = containing_type
477    self.message_type = message_type
478    self.enum_type = enum_type
479    self.is_extension = is_extension
480    self.extension_scope = extension_scope
481    self.containing_oneof = containing_oneof
482    if api_implementation.Type() == 'cpp':
483      if is_extension:
484        if api_implementation.Version() == 2:
485          # pylint: disable=protected-access
486          self._cdescriptor = (
487              _message.Message._GetExtensionDescriptor(full_name))
488          # pylint: enable=protected-access
489        else:
490          self._cdescriptor = cpp_message.GetExtensionDescriptor(full_name)
491      else:
492        if api_implementation.Version() == 2:
493          # pylint: disable=protected-access
494          self._cdescriptor = _message.Message._GetFieldDescriptor(full_name)
495          # pylint: enable=protected-access
496        else:
497          self._cdescriptor = cpp_message.GetFieldDescriptor(full_name)
498    else:
499      self._cdescriptor = None
500
501  @staticmethod
502  def ProtoTypeToCppProtoType(proto_type):
503    """Converts from a Python proto type to a C++ Proto Type.
504
505    The Python ProtocolBuffer classes specify both the 'Python' datatype and the
506    'C++' datatype - and they're not the same. This helper method should
507    translate from one to another.
508
509    Args:
510      proto_type: the Python proto type (descriptor.FieldDescriptor.TYPE_*)
511    Returns:
512      descriptor.FieldDescriptor.CPPTYPE_*, the C++ type.
513    Raises:
514      TypeTransformationError: when the Python proto type isn't known.
515    """
516    try:
517      return FieldDescriptor._PYTHON_TO_CPP_PROTO_TYPE_MAP[proto_type]
518    except KeyError:
519      raise TypeTransformationError('Unknown proto_type: %s' % proto_type)
520
521
522class EnumDescriptor(_NestedDescriptorBase):
523
524  """Descriptor for an enum defined in a .proto file.
525
526  An EnumDescriptor instance has the following attributes:
527
528    name: (str) Name of the enum type.
529    full_name: (str) Full name of the type, including package name
530      and any enclosing type(s).
531
532    values: (list of EnumValueDescriptors) List of the values
533      in this enum.
534    values_by_name: (dict str -> EnumValueDescriptor) Same as |values|,
535      but indexed by the "name" field of each EnumValueDescriptor.
536    values_by_number: (dict int -> EnumValueDescriptor) Same as |values|,
537      but indexed by the "number" field of each EnumValueDescriptor.
538    containing_type: (Descriptor) Descriptor of the immediate containing
539      type of this enum, or None if this is an enum defined at the
540      top level in a .proto file.  Set by Descriptor's constructor
541      if we're passed into one.
542    file: (FileDescriptor) Reference to file descriptor.
543    options: (descriptor_pb2.EnumOptions) Enum options message or
544      None to use default enum options.
545  """
546
547  def __init__(self, name, full_name, filename, values,
548               containing_type=None, options=None, file=None,
549               serialized_start=None, serialized_end=None):
550    """Arguments are as described in the attribute description above.
551
552    Note that filename is an obsolete argument, that is not used anymore.
553    Please use file.name to access this as an attribute.
554    """
555    super(EnumDescriptor, self).__init__(
556        options, 'EnumOptions', name, full_name, file,
557        containing_type, serialized_start=serialized_start,
558        serialized_end=serialized_end)
559
560    self.values = values
561    for value in self.values:
562      value.type = self
563    self.values_by_name = dict((v.name, v) for v in values)
564    self.values_by_number = dict((v.number, v) for v in values)
565
566  def CopyToProto(self, proto):
567    """Copies this to a descriptor_pb2.EnumDescriptorProto.
568
569    Args:
570      proto: An empty descriptor_pb2.EnumDescriptorProto.
571    """
572    # This function is overriden to give a better doc comment.
573    super(EnumDescriptor, self).CopyToProto(proto)
574
575
576class EnumValueDescriptor(DescriptorBase):
577
578  """Descriptor for a single value within an enum.
579
580    name: (str) Name of this value.
581    index: (int) Dense, 0-indexed index giving the order that this
582      value appears textually within its enum in the .proto file.
583    number: (int) Actual number assigned to this enum value.
584    type: (EnumDescriptor) EnumDescriptor to which this value
585      belongs.  Set by EnumDescriptor's constructor if we're
586      passed into one.
587    options: (descriptor_pb2.EnumValueOptions) Enum value options message or
588      None to use default enum value options options.
589  """
590
591  def __init__(self, name, index, number, type=None, options=None):
592    """Arguments are as described in the attribute description above."""
593    super(EnumValueDescriptor, self).__init__(options, 'EnumValueOptions')
594    self.name = name
595    self.index = index
596    self.number = number
597    self.type = type
598
599
600class OneofDescriptor(object):
601  """Descriptor for a oneof field.
602
603    name: (str) Name of the oneof field.
604    full_name: (str) Full name of the oneof field, including package name.
605    index: (int) 0-based index giving the order of the oneof field inside
606      its containing type.
607    containing_type: (Descriptor) Descriptor of the protocol message
608      type that contains this field.  Set by the Descriptor constructor
609      if we're passed into one.
610    fields: (list of FieldDescriptor) The list of field descriptors this
611      oneof can contain.
612  """
613
614  def __init__(self, name, full_name, index, containing_type, fields):
615    """Arguments are as described in the attribute description above."""
616    self.name = name
617    self.full_name = full_name
618    self.index = index
619    self.containing_type = containing_type
620    self.fields = fields
621
622
623class ServiceDescriptor(_NestedDescriptorBase):
624
625  """Descriptor for a service.
626
627    name: (str) Name of the service.
628    full_name: (str) Full name of the service, including package name.
629    index: (int) 0-indexed index giving the order that this services
630      definition appears withing the .proto file.
631    methods: (list of MethodDescriptor) List of methods provided by this
632      service.
633    options: (descriptor_pb2.ServiceOptions) Service options message or
634      None to use default service options.
635    file: (FileDescriptor) Reference to file info.
636  """
637
638  def __init__(self, name, full_name, index, methods, options=None, file=None,
639               serialized_start=None, serialized_end=None):
640    super(ServiceDescriptor, self).__init__(
641        options, 'ServiceOptions', name, full_name, file,
642        None, serialized_start=serialized_start,
643        serialized_end=serialized_end)
644    self.index = index
645    self.methods = methods
646    # Set the containing service for each method in this service.
647    for method in self.methods:
648      method.containing_service = self
649
650  def FindMethodByName(self, name):
651    """Searches for the specified method, and returns its descriptor."""
652    for method in self.methods:
653      if name == method.name:
654        return method
655    return None
656
657  def CopyToProto(self, proto):
658    """Copies this to a descriptor_pb2.ServiceDescriptorProto.
659
660    Args:
661      proto: An empty descriptor_pb2.ServiceDescriptorProto.
662    """
663    # This function is overriden to give a better doc comment.
664    super(ServiceDescriptor, self).CopyToProto(proto)
665
666
667class MethodDescriptor(DescriptorBase):
668
669  """Descriptor for a method in a service.
670
671  name: (str) Name of the method within the service.
672  full_name: (str) Full name of method.
673  index: (int) 0-indexed index of the method inside the service.
674  containing_service: (ServiceDescriptor) The service that contains this
675    method.
676  input_type: The descriptor of the message that this method accepts.
677  output_type: The descriptor of the message that this method returns.
678  options: (descriptor_pb2.MethodOptions) Method options message or
679    None to use default method options.
680  """
681
682  def __init__(self, name, full_name, index, containing_service,
683               input_type, output_type, options=None):
684    """The arguments are as described in the description of MethodDescriptor
685    attributes above.
686
687    Note that containing_service may be None, and may be set later if necessary.
688    """
689    super(MethodDescriptor, self).__init__(options, 'MethodOptions')
690    self.name = name
691    self.full_name = full_name
692    self.index = index
693    self.containing_service = containing_service
694    self.input_type = input_type
695    self.output_type = output_type
696
697
698class FileDescriptor(DescriptorBase):
699  """Descriptor for a file. Mimics the descriptor_pb2.FileDescriptorProto.
700
701  Note that enum_types_by_name, extensions_by_name, and dependencies
702  fields are only set by the message_factory module, and not by the
703  generated proto code.
704
705  name: name of file, relative to root of source tree.
706  package: name of the package
707  serialized_pb: (str) Byte string of serialized
708    descriptor_pb2.FileDescriptorProto.
709  dependencies: List of other FileDescriptors this FileDescriptor depends on.
710  message_types_by_name: Dict of message names of their descriptors.
711  enum_types_by_name: Dict of enum names and their descriptors.
712  extensions_by_name: Dict of extension names and their descriptors.
713  """
714
715  def __init__(self, name, package, options=None, serialized_pb=None,
716               dependencies=None):
717    """Constructor."""
718    super(FileDescriptor, self).__init__(options, 'FileOptions')
719
720    self.message_types_by_name = {}
721    self.name = name
722    self.package = package
723    self.serialized_pb = serialized_pb
724
725    self.enum_types_by_name = {}
726    self.extensions_by_name = {}
727    self.dependencies = (dependencies or [])
728
729    if (api_implementation.Type() == 'cpp' and
730        self.serialized_pb is not None):
731      if api_implementation.Version() == 2:
732        # pylint: disable=protected-access
733        _message.Message._BuildFile(self.serialized_pb)
734        # pylint: enable=protected-access
735      else:
736        cpp_message.BuildFile(self.serialized_pb)
737
738  def CopyToProto(self, proto):
739    """Copies this to a descriptor_pb2.FileDescriptorProto.
740
741    Args:
742      proto: An empty descriptor_pb2.FileDescriptorProto.
743    """
744    proto.ParseFromString(self.serialized_pb)
745
746
747def _ParseOptions(message, string):
748  """Parses serialized options.
749
750  This helper function is used to parse serialized options in generated
751  proto2 files. It must not be used outside proto2.
752  """
753  message.ParseFromString(string)
754  return message
755
756
757def MakeDescriptor(desc_proto, package='', build_file_if_cpp=True):
758  """Make a protobuf Descriptor given a DescriptorProto protobuf.
759
760  Handles nested descriptors. Note that this is limited to the scope of defining
761  a message inside of another message. Composite fields can currently only be
762  resolved if the message is defined in the same scope as the field.
763
764  Args:
765    desc_proto: The descriptor_pb2.DescriptorProto protobuf message.
766    package: Optional package name for the new message Descriptor (string).
767    build_file_if_cpp: Update the C++ descriptor pool if api matches.
768                       Set to False on recursion, so no duplicates are created.
769  Returns:
770    A Descriptor for protobuf messages.
771  """
772  if api_implementation.Type() == 'cpp' and build_file_if_cpp:
773    # The C++ implementation requires all descriptors to be backed by the same
774    # definition in the C++ descriptor pool. To do this, we build a
775    # FileDescriptorProto with the same definition as this descriptor and build
776    # it into the pool.
777    from google.protobuf import descriptor_pb2
778    file_descriptor_proto = descriptor_pb2.FileDescriptorProto()
779    file_descriptor_proto.message_type.add().MergeFrom(desc_proto)
780
781    # Generate a random name for this proto file to prevent conflicts with
782    # any imported ones. We need to specify a file name so BuildFile accepts
783    # our FileDescriptorProto, but it is not important what that file name
784    # is actually set to.
785    proto_name = str(uuid.uuid4())
786
787    if package:
788      file_descriptor_proto.name = os.path.join(package.replace('.', '/'),
789                                                proto_name + '.proto')
790      file_descriptor_proto.package = package
791    else:
792      file_descriptor_proto.name = proto_name + '.proto'
793
794    if api_implementation.Version() == 2:
795      # pylint: disable=protected-access
796      _message.Message._BuildFile(file_descriptor_proto.SerializeToString())
797      # pylint: enable=protected-access
798    else:
799      cpp_message.BuildFile(file_descriptor_proto.SerializeToString())
800
801  full_message_name = [desc_proto.name]
802  if package: full_message_name.insert(0, package)
803
804  # Create Descriptors for enum types
805  enum_types = {}
806  for enum_proto in desc_proto.enum_type:
807    full_name = '.'.join(full_message_name + [enum_proto.name])
808    enum_desc = EnumDescriptor(
809      enum_proto.name, full_name, None, [
810          EnumValueDescriptor(enum_val.name, ii, enum_val.number)
811          for ii, enum_val in enumerate(enum_proto.value)])
812    enum_types[full_name] = enum_desc
813
814  # Create Descriptors for nested types
815  nested_types = {}
816  for nested_proto in desc_proto.nested_type:
817    full_name = '.'.join(full_message_name + [nested_proto.name])
818    # Nested types are just those defined inside of the message, not all types
819    # used by fields in the message, so no loops are possible here.
820    nested_desc = MakeDescriptor(nested_proto,
821                                 package='.'.join(full_message_name),
822                                 build_file_if_cpp=False)
823    nested_types[full_name] = nested_desc
824
825  fields = []
826  for field_proto in desc_proto.field:
827    full_name = '.'.join(full_message_name + [field_proto.name])
828    enum_desc = None
829    nested_desc = None
830    if field_proto.HasField('type_name'):
831      type_name = field_proto.type_name
832      full_type_name = '.'.join(full_message_name +
833                                [type_name[type_name.rfind('.')+1:]])
834      if full_type_name in nested_types:
835        nested_desc = nested_types[full_type_name]
836      elif full_type_name in enum_types:
837        enum_desc = enum_types[full_type_name]
838      # Else type_name references a non-local type, which isn't implemented
839    field = FieldDescriptor(
840        field_proto.name, full_name, field_proto.number - 1,
841        field_proto.number, field_proto.type,
842        FieldDescriptor.ProtoTypeToCppProtoType(field_proto.type),
843        field_proto.label, None, nested_desc, enum_desc, None, False, None,
844        has_default_value=False)
845    fields.append(field)
846
847  desc_name = '.'.join(full_message_name)
848  return Descriptor(desc_proto.name, desc_name, None, None, fields,
849                    nested_types.values(), enum_types.values(), [])
850