cpp_message.py revision ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16
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"""Contains helper functions used to create protocol message classes from
32Descriptor objects at runtime backed by the protocol buffer C++ API.
33"""
34
35__author__ = 'petar@google.com (Petar Petrov)'
36
37import copy_reg
38import operator
39from google.protobuf.internal import _net_proto2___python
40from google.protobuf.internal import enum_type_wrapper
41from google.protobuf import message
42
43
44_LABEL_REPEATED = _net_proto2___python.LABEL_REPEATED
45_LABEL_OPTIONAL = _net_proto2___python.LABEL_OPTIONAL
46_CPPTYPE_MESSAGE = _net_proto2___python.CPPTYPE_MESSAGE
47_TYPE_MESSAGE = _net_proto2___python.TYPE_MESSAGE
48
49
50def GetDescriptorPool():
51  """Creates a new DescriptorPool C++ object."""
52  return _net_proto2___python.NewCDescriptorPool()
53
54
55_pool = GetDescriptorPool()
56
57
58def GetFieldDescriptor(full_field_name):
59  """Searches for a field descriptor given a full field name."""
60  return _pool.FindFieldByName(full_field_name)
61
62
63def BuildFile(content):
64  """Registers a new proto file in the underlying C++ descriptor pool."""
65  _net_proto2___python.BuildFile(content)
66
67
68def GetExtensionDescriptor(full_extension_name):
69  """Searches for extension descriptor given a full field name."""
70  return _pool.FindExtensionByName(full_extension_name)
71
72
73def NewCMessage(full_message_name):
74  """Creates a new C++ protocol message by its name."""
75  return _net_proto2___python.NewCMessage(full_message_name)
76
77
78def ScalarProperty(cdescriptor):
79  """Returns a scalar property for the given descriptor."""
80
81  def Getter(self):
82    return self._cmsg.GetScalar(cdescriptor)
83
84  def Setter(self, value):
85    self._cmsg.SetScalar(cdescriptor, value)
86
87  return property(Getter, Setter)
88
89
90def CompositeProperty(cdescriptor, message_type):
91  """Returns a Python property the given composite field."""
92
93  def Getter(self):
94    sub_message = self._composite_fields.get(cdescriptor.name, None)
95    if sub_message is None:
96      cmessage = self._cmsg.NewSubMessage(cdescriptor)
97      sub_message = message_type._concrete_class(__cmessage=cmessage)
98      self._composite_fields[cdescriptor.name] = sub_message
99    return sub_message
100
101  return property(Getter)
102
103
104class RepeatedScalarContainer(object):
105  """Container for repeated scalar fields."""
106
107  __slots__ = ['_message', '_cfield_descriptor', '_cmsg']
108
109  def __init__(self, msg, cfield_descriptor):
110    self._message = msg
111    self._cmsg = msg._cmsg
112    self._cfield_descriptor = cfield_descriptor
113
114  def append(self, value):
115    self._cmsg.AddRepeatedScalar(
116        self._cfield_descriptor, value)
117
118  def extend(self, sequence):
119    for element in sequence:
120      self.append(element)
121
122  def insert(self, key, value):
123    values = self[slice(None, None, None)]
124    values.insert(key, value)
125    self._cmsg.AssignRepeatedScalar(self._cfield_descriptor, values)
126
127  def remove(self, value):
128    values = self[slice(None, None, None)]
129    values.remove(value)
130    self._cmsg.AssignRepeatedScalar(self._cfield_descriptor, values)
131
132  def __setitem__(self, key, value):
133    values = self[slice(None, None, None)]
134    values[key] = value
135    self._cmsg.AssignRepeatedScalar(self._cfield_descriptor, values)
136
137  def __getitem__(self, key):
138    return self._cmsg.GetRepeatedScalar(self._cfield_descriptor, key)
139
140  def __delitem__(self, key):
141    self._cmsg.DeleteRepeatedField(self._cfield_descriptor, key)
142
143  def __len__(self):
144    return len(self[slice(None, None, None)])
145
146  def __eq__(self, other):
147    if self is other:
148      return True
149    if not operator.isSequenceType(other):
150      raise TypeError(
151          'Can only compare repeated scalar fields against sequences.')
152    # We are presumably comparing against some other sequence type.
153    return other == self[slice(None, None, None)]
154
155  def __ne__(self, other):
156    return not self == other
157
158  def __hash__(self):
159    raise TypeError('unhashable object')
160
161  def sort(self, *args, **kwargs):
162    # Maintain compatibility with the previous interface.
163    if 'sort_function' in kwargs:
164      kwargs['cmp'] = kwargs.pop('sort_function')
165    self._cmsg.AssignRepeatedScalar(self._cfield_descriptor,
166                                    sorted(self, *args, **kwargs))
167
168
169def RepeatedScalarProperty(cdescriptor):
170  """Returns a Python property the given repeated scalar field."""
171
172  def Getter(self):
173    container = self._composite_fields.get(cdescriptor.name, None)
174    if container is None:
175      container = RepeatedScalarContainer(self, cdescriptor)
176      self._composite_fields[cdescriptor.name] = container
177    return container
178
179  def Setter(self, new_value):
180    raise AttributeError('Assignment not allowed to repeated field '
181                         '"%s" in protocol message object.' % cdescriptor.name)
182
183  doc = 'Magic attribute generated for "%s" proto field.' % cdescriptor.name
184  return property(Getter, Setter, doc=doc)
185
186
187class RepeatedCompositeContainer(object):
188  """Container for repeated composite fields."""
189
190  __slots__ = ['_message', '_subclass', '_cfield_descriptor', '_cmsg']
191
192  def __init__(self, msg, cfield_descriptor, subclass):
193    self._message = msg
194    self._cmsg = msg._cmsg
195    self._subclass = subclass
196    self._cfield_descriptor = cfield_descriptor
197
198  def add(self, **kwargs):
199    cmessage = self._cmsg.AddMessage(self._cfield_descriptor)
200    return self._subclass(__cmessage=cmessage, __owner=self._message, **kwargs)
201
202  def extend(self, elem_seq):
203    """Extends by appending the given sequence of elements of the same type
204    as this one, copying each individual message.
205    """
206    for message in elem_seq:
207      self.add().MergeFrom(message)
208
209  def remove(self, value):
210    # TODO(protocol-devel): This is inefficient as it needs to generate a
211    # message pointer for each message only to do index().  Move this to a C++
212    # extension function.
213    self.__delitem__(self[slice(None, None, None)].index(value))
214
215  def MergeFrom(self, other):
216    for message in other[:]:
217      self.add().MergeFrom(message)
218
219  def __getitem__(self, key):
220    cmessages = self._cmsg.GetRepeatedMessage(
221        self._cfield_descriptor, key)
222    subclass = self._subclass
223    if not isinstance(cmessages, list):
224      return subclass(__cmessage=cmessages, __owner=self._message)
225
226    return [subclass(__cmessage=m, __owner=self._message) for m in cmessages]
227
228  def __delitem__(self, key):
229    self._cmsg.DeleteRepeatedField(
230        self._cfield_descriptor, key)
231
232  def __len__(self):
233    return self._cmsg.FieldLength(self._cfield_descriptor)
234
235  def __eq__(self, other):
236    """Compares the current instance with another one."""
237    if self is other:
238      return True
239    if not isinstance(other, self.__class__):
240      raise TypeError('Can only compare repeated composite fields against '
241                      'other repeated composite fields.')
242    messages = self[slice(None, None, None)]
243    other_messages = other[slice(None, None, None)]
244    return messages == other_messages
245
246  def __hash__(self):
247    raise TypeError('unhashable object')
248
249  def sort(self, cmp=None, key=None, reverse=False, **kwargs):
250    # Maintain compatibility with the old interface.
251    if cmp is None and 'sort_function' in kwargs:
252      cmp = kwargs.pop('sort_function')
253
254    # The cmp function, if provided, is passed the results of the key function,
255    # so we only need to wrap one of them.
256    if key is None:
257      index_key = self.__getitem__
258    else:
259      index_key = lambda i: key(self[i])
260
261    # Sort the list of current indexes by the underlying object.
262    indexes = range(len(self))
263    indexes.sort(cmp=cmp, key=index_key, reverse=reverse)
264
265    # Apply the transposition.
266    for dest, src in enumerate(indexes):
267      if dest == src:
268        continue
269      self._cmsg.SwapRepeatedFieldElements(self._cfield_descriptor, dest, src)
270      # Don't swap the same value twice.
271      indexes[src] = src
272
273
274def RepeatedCompositeProperty(cdescriptor, message_type):
275  """Returns a Python property for the given repeated composite field."""
276
277  def Getter(self):
278    container = self._composite_fields.get(cdescriptor.name, None)
279    if container is None:
280      container = RepeatedCompositeContainer(
281          self, cdescriptor, message_type._concrete_class)
282      self._composite_fields[cdescriptor.name] = container
283    return container
284
285  def Setter(self, new_value):
286    raise AttributeError('Assignment not allowed to repeated field '
287                         '"%s" in protocol message object.' % cdescriptor.name)
288
289  doc = 'Magic attribute generated for "%s" proto field.' % cdescriptor.name
290  return property(Getter, Setter, doc=doc)
291
292
293class ExtensionDict(object):
294  """Extension dictionary added to each protocol message."""
295
296  def __init__(self, msg):
297    self._message = msg
298    self._cmsg = msg._cmsg
299    self._values = {}
300
301  def __setitem__(self, extension, value):
302    from google.protobuf import descriptor
303    if not isinstance(extension, descriptor.FieldDescriptor):
304      raise KeyError('Bad extension %r.' % (extension,))
305    cdescriptor = extension._cdescriptor
306    if (cdescriptor.label != _LABEL_OPTIONAL or
307        cdescriptor.cpp_type == _CPPTYPE_MESSAGE):
308      raise TypeError('Extension %r is repeated and/or a composite type.' % (
309          extension.full_name,))
310    self._cmsg.SetScalar(cdescriptor, value)
311    self._values[extension] = value
312
313  def __getitem__(self, extension):
314    from google.protobuf import descriptor
315    if not isinstance(extension, descriptor.FieldDescriptor):
316      raise KeyError('Bad extension %r.' % (extension,))
317
318    cdescriptor = extension._cdescriptor
319    if (cdescriptor.label != _LABEL_REPEATED and
320        cdescriptor.cpp_type != _CPPTYPE_MESSAGE):
321      return self._cmsg.GetScalar(cdescriptor)
322
323    ext = self._values.get(extension, None)
324    if ext is not None:
325      return ext
326
327    ext = self._CreateNewHandle(extension)
328    self._values[extension] = ext
329    return ext
330
331  def ClearExtension(self, extension):
332    from google.protobuf import descriptor
333    if not isinstance(extension, descriptor.FieldDescriptor):
334      raise KeyError('Bad extension %r.' % (extension,))
335    self._cmsg.ClearFieldByDescriptor(extension._cdescriptor)
336    if extension in self._values:
337      del self._values[extension]
338
339  def HasExtension(self, extension):
340    from google.protobuf import descriptor
341    if not isinstance(extension, descriptor.FieldDescriptor):
342      raise KeyError('Bad extension %r.' % (extension,))
343    return self._cmsg.HasFieldByDescriptor(extension._cdescriptor)
344
345  def _FindExtensionByName(self, name):
346    """Tries to find a known extension with the specified name.
347
348    Args:
349      name: Extension full name.
350
351    Returns:
352      Extension field descriptor.
353    """
354    return self._message._extensions_by_name.get(name, None)
355
356  def _CreateNewHandle(self, extension):
357    cdescriptor = extension._cdescriptor
358    if (cdescriptor.label != _LABEL_REPEATED and
359        cdescriptor.cpp_type == _CPPTYPE_MESSAGE):
360      cmessage = self._cmsg.NewSubMessage(cdescriptor)
361      return extension.message_type._concrete_class(__cmessage=cmessage)
362
363    if cdescriptor.label == _LABEL_REPEATED:
364      if cdescriptor.cpp_type == _CPPTYPE_MESSAGE:
365        return RepeatedCompositeContainer(
366            self._message, cdescriptor, extension.message_type._concrete_class)
367      else:
368        return RepeatedScalarContainer(self._message, cdescriptor)
369    # This shouldn't happen!
370    assert False
371    return None
372
373
374def NewMessage(bases, message_descriptor, dictionary):
375  """Creates a new protocol message *class*."""
376  _AddClassAttributesForNestedExtensions(message_descriptor, dictionary)
377  _AddEnumValues(message_descriptor, dictionary)
378  _AddDescriptors(message_descriptor, dictionary)
379  return bases
380
381
382def InitMessage(message_descriptor, cls):
383  """Constructs a new message instance (called before instance's __init__)."""
384  cls._extensions_by_name = {}
385  _AddInitMethod(message_descriptor, cls)
386  _AddMessageMethods(message_descriptor, cls)
387  _AddPropertiesForExtensions(message_descriptor, cls)
388  copy_reg.pickle(cls, lambda obj: (cls, (), obj.__getstate__()))
389
390
391def _AddDescriptors(message_descriptor, dictionary):
392  """Sets up a new protocol message class dictionary.
393
394  Args:
395    message_descriptor: A Descriptor instance describing this message type.
396    dictionary: Class dictionary to which we'll add a '__slots__' entry.
397  """
398  dictionary['__descriptors'] = {}
399  for field in message_descriptor.fields:
400    dictionary['__descriptors'][field.name] = GetFieldDescriptor(
401        field.full_name)
402
403  dictionary['__slots__'] = list(dictionary['__descriptors'].iterkeys()) + [
404      '_cmsg', '_owner', '_composite_fields', 'Extensions', '_HACK_REFCOUNTS']
405
406
407def _AddEnumValues(message_descriptor, dictionary):
408  """Sets class-level attributes for all enum fields defined in this message.
409
410  Args:
411    message_descriptor: Descriptor object for this message type.
412    dictionary: Class dictionary that should be populated.
413  """
414  for enum_type in message_descriptor.enum_types:
415    dictionary[enum_type.name] = enum_type_wrapper.EnumTypeWrapper(enum_type)
416    for enum_value in enum_type.values:
417      dictionary[enum_value.name] = enum_value.number
418
419
420def _AddClassAttributesForNestedExtensions(message_descriptor, dictionary):
421  """Adds class attributes for the nested extensions."""
422  extension_dict = message_descriptor.extensions_by_name
423  for extension_name, extension_field in extension_dict.iteritems():
424    assert extension_name not in dictionary
425    dictionary[extension_name] = extension_field
426
427
428def _AddInitMethod(message_descriptor, cls):
429  """Adds an __init__ method to cls."""
430
431  # Create and attach message field properties to the message class.
432  # This can be done just once per message class, since property setters and
433  # getters are passed the message instance.
434  # This makes message instantiation extremely fast, and at the same time it
435  # doesn't require the creation of property objects for each message instance,
436  # which saves a lot of memory.
437  for field in message_descriptor.fields:
438    field_cdescriptor = cls.__descriptors[field.name]
439    if field.label == _LABEL_REPEATED:
440      if field.cpp_type == _CPPTYPE_MESSAGE:
441        value = RepeatedCompositeProperty(field_cdescriptor, field.message_type)
442      else:
443        value = RepeatedScalarProperty(field_cdescriptor)
444    elif field.cpp_type == _CPPTYPE_MESSAGE:
445      value = CompositeProperty(field_cdescriptor, field.message_type)
446    else:
447      value = ScalarProperty(field_cdescriptor)
448    setattr(cls, field.name, value)
449
450    # Attach a constant with the field number.
451    constant_name = field.name.upper() + '_FIELD_NUMBER'
452    setattr(cls, constant_name, field.number)
453
454  def Init(self, **kwargs):
455    """Message constructor."""
456    cmessage = kwargs.pop('__cmessage', None)
457    if cmessage:
458      self._cmsg = cmessage
459    else:
460      self._cmsg = NewCMessage(message_descriptor.full_name)
461
462    # Keep a reference to the owner, as the owner keeps a reference to the
463    # underlying protocol buffer message.
464    owner = kwargs.pop('__owner', None)
465    if owner:
466      self._owner = owner
467
468    if message_descriptor.is_extendable:
469      self.Extensions = ExtensionDict(self)
470    else:
471      # Reference counting in the C++ code is broken and depends on
472      # the Extensions reference to keep this object alive during unit
473      # tests (see b/4856052).  Remove this once b/4945904 is fixed.
474      self._HACK_REFCOUNTS = self
475    self._composite_fields = {}
476
477    for field_name, field_value in kwargs.iteritems():
478      field_cdescriptor = self.__descriptors.get(field_name, None)
479      if not field_cdescriptor:
480        raise ValueError('Protocol message has no "%s" field.' % field_name)
481      if field_cdescriptor.label == _LABEL_REPEATED:
482        if field_cdescriptor.cpp_type == _CPPTYPE_MESSAGE:
483          field_name = getattr(self, field_name)
484          for val in field_value:
485            field_name.add().MergeFrom(val)
486        else:
487          getattr(self, field_name).extend(field_value)
488      elif field_cdescriptor.cpp_type == _CPPTYPE_MESSAGE:
489        getattr(self, field_name).MergeFrom(field_value)
490      else:
491        setattr(self, field_name, field_value)
492
493  Init.__module__ = None
494  Init.__doc__ = None
495  cls.__init__ = Init
496
497
498def _IsMessageSetExtension(field):
499  """Checks if a field is a message set extension."""
500  return (field.is_extension and
501          field.containing_type.has_options and
502          field.containing_type.GetOptions().message_set_wire_format and
503          field.type == _TYPE_MESSAGE and
504          field.message_type == field.extension_scope and
505          field.label == _LABEL_OPTIONAL)
506
507
508def _AddMessageMethods(message_descriptor, cls):
509  """Adds the methods to a protocol message class."""
510  if message_descriptor.is_extendable:
511
512    def ClearExtension(self, extension):
513      self.Extensions.ClearExtension(extension)
514
515    def HasExtension(self, extension):
516      return self.Extensions.HasExtension(extension)
517
518  def HasField(self, field_name):
519    return self._cmsg.HasField(field_name)
520
521  def ClearField(self, field_name):
522    child_cmessage = None
523    if field_name in self._composite_fields:
524      child_field = self._composite_fields[field_name]
525      del self._composite_fields[field_name]
526
527      child_cdescriptor = self.__descriptors[field_name]
528      # TODO(anuraag): Support clearing repeated message fields as well.
529      if (child_cdescriptor.label != _LABEL_REPEATED and
530          child_cdescriptor.cpp_type == _CPPTYPE_MESSAGE):
531        child_field._owner = None
532        child_cmessage = child_field._cmsg
533
534    if child_cmessage is not None:
535      self._cmsg.ClearField(field_name, child_cmessage)
536    else:
537      self._cmsg.ClearField(field_name)
538
539  def Clear(self):
540    cmessages_to_release = []
541    for field_name, child_field in self._composite_fields.iteritems():
542      child_cdescriptor = self.__descriptors[field_name]
543      # TODO(anuraag): Support clearing repeated message fields as well.
544      if (child_cdescriptor.label != _LABEL_REPEATED and
545          child_cdescriptor.cpp_type == _CPPTYPE_MESSAGE):
546        child_field._owner = None
547        cmessages_to_release.append((child_cdescriptor, child_field._cmsg))
548    self._composite_fields.clear()
549    self._cmsg.Clear(cmessages_to_release)
550
551  def IsInitialized(self, errors=None):
552    if self._cmsg.IsInitialized():
553      return True
554    if errors is not None:
555      errors.extend(self.FindInitializationErrors());
556    return False
557
558  def SerializeToString(self):
559    if not self.IsInitialized():
560      raise message.EncodeError(
561          'Message %s is missing required fields: %s' % (
562          self._cmsg.full_name, ','.join(self.FindInitializationErrors())))
563    return self._cmsg.SerializeToString()
564
565  def SerializePartialToString(self):
566    return self._cmsg.SerializePartialToString()
567
568  def ParseFromString(self, serialized):
569    self.Clear()
570    self.MergeFromString(serialized)
571
572  def MergeFromString(self, serialized):
573    byte_size = self._cmsg.MergeFromString(serialized)
574    if byte_size < 0:
575      raise message.DecodeError('Unable to merge from string.')
576    return byte_size
577
578  def MergeFrom(self, msg):
579    if not isinstance(msg, cls):
580      raise TypeError(
581          "Parameter to MergeFrom() must be instance of same class: "
582          "expected %s got %s." % (cls.__name__, type(msg).__name__))
583    self._cmsg.MergeFrom(msg._cmsg)
584
585  def CopyFrom(self, msg):
586    self._cmsg.CopyFrom(msg._cmsg)
587
588  def ByteSize(self):
589    return self._cmsg.ByteSize()
590
591  def SetInParent(self):
592    return self._cmsg.SetInParent()
593
594  def ListFields(self):
595    all_fields = []
596    field_list = self._cmsg.ListFields()
597    fields_by_name = cls.DESCRIPTOR.fields_by_name
598    for is_extension, field_name in field_list:
599      if is_extension:
600        extension = cls._extensions_by_name[field_name]
601        all_fields.append((extension, self.Extensions[extension]))
602      else:
603        field_descriptor = fields_by_name[field_name]
604        all_fields.append(
605            (field_descriptor, getattr(self, field_name)))
606    all_fields.sort(key=lambda item: item[0].number)
607    return all_fields
608
609  def FindInitializationErrors(self):
610    return self._cmsg.FindInitializationErrors()
611
612  def __str__(self):
613    return self._cmsg.DebugString()
614
615  def __eq__(self, other):
616    if self is other:
617      return True
618    if not isinstance(other, self.__class__):
619      return False
620    return self.ListFields() == other.ListFields()
621
622  def __ne__(self, other):
623    return not self == other
624
625  def __hash__(self):
626    raise TypeError('unhashable object')
627
628  def __unicode__(self):
629    # Lazy import to prevent circular import when text_format imports this file.
630    from google.protobuf import text_format
631    return text_format.MessageToString(self, as_utf8=True).decode('utf-8')
632
633  # Attach the local methods to the message class.
634  for key, value in locals().copy().iteritems():
635    if key not in ('key', 'value', '__builtins__', '__name__', '__doc__'):
636      setattr(cls, key, value)
637
638  # Static methods:
639
640  def RegisterExtension(extension_handle):
641    extension_handle.containing_type = cls.DESCRIPTOR
642    cls._extensions_by_name[extension_handle.full_name] = extension_handle
643
644    if _IsMessageSetExtension(extension_handle):
645      # MessageSet extension.  Also register under type name.
646      cls._extensions_by_name[
647          extension_handle.message_type.full_name] = extension_handle
648  cls.RegisterExtension = staticmethod(RegisterExtension)
649
650  def FromString(string):
651    msg = cls()
652    msg.MergeFromString(string)
653    return msg
654  cls.FromString = staticmethod(FromString)
655
656
657
658def _AddPropertiesForExtensions(message_descriptor, cls):
659  """Adds properties for all fields in this protocol message type."""
660  extension_dict = message_descriptor.extensions_by_name
661  for extension_name, extension_field in extension_dict.iteritems():
662    constant_name = extension_name.upper() + '_FIELD_NUMBER'
663    setattr(cls, constant_name, extension_field.number)
664