descriptor.py revision d0332953cda33fb4f8e24ebff9c49159b69c43d6
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
42file, in types that make this information accessible in Python.
43"""
44
45__author__ = 'robinson@google.com (Will Robinson)'
46
47
48class Error(Exception):
49  """Base error for this module."""
50
51
52class 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
96class _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
159class 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...
277class 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
401class 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
458class 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
482class 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
526class 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
557class 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
583def _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