1# Protocol Buffers - Google's data interchange format
2# Copyright 2008 Google Inc. All rights reserved.
3#
4# Use of this source code is governed by a BSD-style
5# license that can be found in the LICENSE file or at
6# https://developers.google.com/open-source/licenses/bsd
7
8# This code is meant to work on Python 2.4 and above only.
9#
10# TODO: Helpers for verbose, common checks like seeing if a
11# descriptor's cpp_type is CPPTYPE_MESSAGE.
12"""Contains a metaclass and helper functions used to create
13
14protocol message classes from Descriptor objects at runtime.
15
16Recall that a metaclass is the "type" of a class.
17(A class is to a metaclass what an instance is to a class.)
18
19In this case, we use the GeneratedProtocolMessageType metaclass
20to inject all the useful functionality into the classes
21output by the protocol compiler at compile-time.
22
23The upshot of all this is that the real implementation
24details for ALL pure-Python protocol buffers are *here in
25this file*.
26"""
27
28__author__ = 'robinson@google.com (Will Robinson)'
29
30import datetime
31from io import BytesIO
32import math
33import struct
34import sys
35import warnings
36import weakref
37
38from google.protobuf import descriptor as descriptor_mod
39from google.protobuf import message as message_mod
40from google.protobuf import text_format
41
42# We use "as" to avoid name collisions with variables.
43from google.protobuf.internal import api_implementation
44from google.protobuf.internal import containers
45from google.protobuf.internal import decoder
46from google.protobuf.internal import encoder
47from google.protobuf.internal import enum_type_wrapper
48from google.protobuf.internal import extension_dict
49from google.protobuf.internal import message_listener as message_listener_mod
50from google.protobuf.internal import type_checkers
51from google.protobuf.internal import well_known_types
52from google.protobuf.internal import wire_format
53
54_FieldDescriptor = descriptor_mod.FieldDescriptor
55_AnyFullTypeName = 'google.protobuf.Any'
56_StructFullTypeName = 'google.protobuf.Struct'
57_ListValueFullTypeName = 'google.protobuf.ListValue'
58_ExtensionDict = extension_dict._ExtensionDict
59
60
61class GeneratedProtocolMessageType(type):
62 """Metaclass for protocol message classes created at runtime from Descriptors.
63
64 We add implementations for all methods described in the Message class. We
65 also create properties to allow getting/setting all fields in the protocol
66 message. Finally, we create slots to prevent users from accidentally
67 "setting" nonexistent fields in the protocol message, which then wouldn't get
68 serialized / deserialized properly.
69
70 The protocol compiler currently uses this metaclass to create protocol
71 message classes at runtime. Clients can also manually create their own
72 classes at runtime, as in this example:
73
74 mydescriptor = Descriptor(.....)
75 factory = symbol_database.Default()
76 factory.pool.AddDescriptor(mydescriptor)
77 MyProtoClass = message_factory.GetMessageClass(mydescriptor)
78 myproto_instance = MyProtoClass()
79 myproto.foo_field = 23
80 ...
81 """
82
83 # Must be consistent with the protocol-compiler code in
84 # proto2/compiler/internal/generator.*.
85 _DESCRIPTOR_KEY = 'DESCRIPTOR'
86
87 def __new__(cls, name, bases, dictionary):
88 """Custom allocation for runtime-generated class types.
89
90 We override __new__ because this is apparently the only place
91 where we can meaningfully set __slots__ on the class we're creating(?).
92 (The interplay between metaclasses and slots is not very well-documented).
93
94 Args:
95 name: Name of the class (ignored, but required by the metaclass protocol).
96 bases: Base classes of the class we're constructing. (Should be
97 message.Message). We ignore this field, but it's required by the
98 metaclass protocol
99 dictionary: The class dictionary of the class we're constructing.
100 dictionary[_DESCRIPTOR_KEY] must contain a Descriptor object describing
101 this protocol message type.
102
103 Returns:
104 Newly-allocated class.
105
106 Raises:
107 RuntimeError: Generated code only work with python cpp extension.
108 """
109 descriptor = dictionary[GeneratedProtocolMessageType._DESCRIPTOR_KEY]
110
111 if isinstance(descriptor, str):
112 raise RuntimeError(
113 'The generated code only work with python cpp '
114 'extension, but it is using pure python runtime.'
115 )
116
117 # If a concrete class already exists for this descriptor, don't try to
118 # create another. Doing so will break any messages that already exist with
119 # the existing class.
120 #
121 # The C++ implementation appears to have its own internal `PyMessageFactory`
122 # to achieve similar results.
123 #
124 # This most commonly happens in `text_format.py` when using descriptors from
125 # a custom pool; it calls message_factory.GetMessageClass() on a
126 # descriptor which already has an existing concrete class.
127 new_class = getattr(descriptor, '_concrete_class', None)
128 if new_class:
129 return new_class
130
131 if descriptor.full_name in well_known_types.WKTBASES:
132 bases += (well_known_types.WKTBASES[descriptor.full_name],)
133 _AddClassAttributesForNestedExtensions(descriptor, dictionary)
134 _AddSlots(descriptor, dictionary)
135
136 superclass = super(GeneratedProtocolMessageType, cls)
137 new_class = superclass.__new__(cls, name, bases, dictionary)
138 return new_class
139
140 def __init__(cls, name, bases, dictionary):
141 """Here we perform the majority of our work on the class.
142
143 We add enum getters, an __init__ method, implementations of all Message
144 methods, and properties for all fields in the protocol type.
145
146 Args:
147 name: Name of the class (ignored, but required by the metaclass protocol).
148 bases: Base classes of the class we're constructing. (Should be
149 message.Message). We ignore this field, but it's required by the
150 metaclass protocol
151 dictionary: The class dictionary of the class we're constructing.
152 dictionary[_DESCRIPTOR_KEY] must contain a Descriptor object describing
153 this protocol message type.
154 """
155 descriptor = dictionary[GeneratedProtocolMessageType._DESCRIPTOR_KEY]
156
157 # If this is an _existing_ class looked up via `_concrete_class` in the
158 # __new__ method above, then we don't need to re-initialize anything.
159 existing_class = getattr(descriptor, '_concrete_class', None)
160 if existing_class:
161 assert existing_class is cls, (
162 'Duplicate `GeneratedProtocolMessageType` created for descriptor %r'
163 % (descriptor.full_name)
164 )
165 return
166
167 cls._message_set_decoders_by_tag = {}
168 cls._fields_by_tag = {}
169 if (
170 descriptor.has_options
171 and descriptor.GetOptions().message_set_wire_format
172 ):
173 cls._message_set_decoders_by_tag[decoder.MESSAGE_SET_ITEM_TAG] = (
174 decoder.MessageSetItemDecoder(descriptor),
175 None,
176 )
177
178 # Attach stuff to each FieldDescriptor for quick lookup later on.
179 for field in descriptor.fields:
180 _AttachFieldHelpers(cls, field)
181
182 if descriptor.is_extendable and hasattr(descriptor.file, 'pool'):
183 extensions = descriptor.file.pool.FindAllExtensions(descriptor)
184 for ext in extensions:
185 _AttachFieldHelpers(cls, ext)
186
187 descriptor._concrete_class = cls # pylint: disable=protected-access
188 _AddEnumValues(descriptor, cls)
189 _AddInitMethod(descriptor, cls)
190 _AddPropertiesForFields(descriptor, cls)
191 _AddPropertiesForExtensions(descriptor, cls)
192 _AddStaticMethods(cls)
193 _AddMessageMethods(descriptor, cls)
194 _AddPrivateHelperMethods(descriptor, cls)
195
196 superclass = super(GeneratedProtocolMessageType, cls)
197 superclass.__init__(name, bases, dictionary)
198
199
200# Stateless helpers for GeneratedProtocolMessageType below.
201# Outside clients should not access these directly.
202#
203# I opted not to make any of these methods on the metaclass, to make it more
204# clear that I'm not really using any state there and to keep clients from
205# thinking that they have direct access to these construction helpers.
206
207
208def _PropertyName(proto_field_name):
209 """Returns the name of the public property attribute which
210
211 clients can use to get and (in some cases) set the value
212 of a protocol message field.
213
214 Args:
215 proto_field_name: The protocol message field name, exactly as it appears (or
216 would appear) in a .proto file.
217 """
218 # TODO: Escape Python keywords (e.g., yield), and test this support.
219 # nnorwitz makes my day by writing:
220 # """
221 # FYI. See the keyword module in the stdlib. This could be as simple as:
222 #
223 # if keyword.iskeyword(proto_field_name):
224 # return proto_field_name + "_"
225 # return proto_field_name
226 # """
227 # Kenton says: The above is a BAD IDEA. People rely on being able to use
228 # getattr() and setattr() to reflectively manipulate field values. If we
229 # rename the properties, then every such user has to also make sure to apply
230 # the same transformation. Note that currently if you name a field "yield",
231 # you can still access it just fine using getattr/setattr -- it's not even
232 # that cumbersome to do so.
233 # TODO: Remove this method entirely if/when everyone agrees with my
234 # position.
235 return proto_field_name
236
237
238def _AddSlots(message_descriptor, dictionary):
239 """Adds a __slots__ entry to dictionary, containing the names of all valid
240
241 attributes for this message type.
242
243 Args:
244 message_descriptor: A Descriptor instance describing this message type.
245 dictionary: Class dictionary to which we'll add a '__slots__' entry.
246 """
247 dictionary['__slots__'] = [
248 '_cached_byte_size',
249 '_cached_byte_size_dirty',
250 '_fields',
251 '_unknown_fields',
252 '_is_present_in_parent',
253 '_listener',
254 '_listener_for_children',
255 '__weakref__',
256 '_oneofs',
257 '_frozen',
258 ]
259
260
261def _IsMessageSetExtension(field):
262 return (
263 field.is_extension
264 and field.containing_type.has_options
265 and field.containing_type.GetOptions().message_set_wire_format
266 and field.type == _FieldDescriptor.TYPE_MESSAGE
267 and not field.is_required
268 and not field.is_repeated
269 )
270
271
272def _IsMapField(field):
273 return (
274 field.type == _FieldDescriptor.TYPE_MESSAGE
275 and field.message_type._is_map_entry
276 )
277
278
279def _IsMessageMapField(field):
280 value_type = field.message_type.fields_by_name['value']
281 return value_type.cpp_type == _FieldDescriptor.CPPTYPE_MESSAGE
282
283
284def _AttachFieldHelpers(cls, field_descriptor):
285 field_descriptor._default_constructor = _DefaultValueConstructorForField(
286 field_descriptor
287 )
288
289 def AddFieldByTag(wiretype, is_packed):
290 tag_bytes = encoder.TagBytes(field_descriptor.number, wiretype)
291 cls._fields_by_tag[tag_bytes] = (field_descriptor, is_packed)
292
293 AddFieldByTag(
294 type_checkers.FIELD_TYPE_TO_WIRE_TYPE[field_descriptor.type], False
295 )
296
297 if field_descriptor.is_repeated and wire_format.IsTypePackable(
298 field_descriptor.type
299 ):
300 # To support wire compatibility of adding packed = true, add a decoder for
301 # packed values regardless of the field's options.
302 AddFieldByTag(wire_format.WIRETYPE_LENGTH_DELIMITED, True)
303
304
305def _MaybeAddEncoder(cls, field_descriptor):
306 if hasattr(field_descriptor, '_encoder'):
307 return
308 is_repeated = field_descriptor.is_repeated
309 is_map_entry = _IsMapField(field_descriptor)
310 is_packed = field_descriptor.is_packed
311
312 if is_map_entry:
313 key_descriptor = field_descriptor.message_type.fields_by_name['key']
314 value_descriptor = field_descriptor.message_type.fields_by_name['value']
315
316 key_sizer = type_checkers.TYPE_TO_SIZER[key_descriptor.type](
317 key_descriptor.number, False, False
318 )
319 value_sizer = type_checkers.TYPE_TO_SIZER[value_descriptor.type](
320 value_descriptor.number, False, False
321 )
322
323 key_encoder = type_checkers.TYPE_TO_ENCODER[key_descriptor.type](
324 key_descriptor.number, False, False
325 )
326 value_encoder = type_checkers.TYPE_TO_ENCODER[value_descriptor.type](
327 value_descriptor.number, False, False
328 )
329
330 field_encoder = encoder.MapEncoder(
331 field_descriptor, key_encoder, value_encoder, key_sizer, value_sizer
332 )
333 sizer = encoder.MapSizer(field_descriptor, key_sizer, value_sizer)
334 elif _IsMessageSetExtension(field_descriptor):
335 field_encoder = encoder.MessageSetItemEncoder(field_descriptor.number)
336 sizer = encoder.MessageSetItemSizer(field_descriptor.number)
337 else:
338 field_encoder = type_checkers.TYPE_TO_ENCODER[field_descriptor.type](
339 field_descriptor.number, is_repeated, is_packed
340 )
341 sizer = type_checkers.TYPE_TO_SIZER[field_descriptor.type](
342 field_descriptor.number, is_repeated, is_packed
343 )
344
345 field_descriptor._sizer = sizer
346 field_descriptor._encoder = field_encoder
347
348
349def _MaybeAddDecoder(cls, field_descriptor):
350 if hasattr(field_descriptor, '_decoders'):
351 return
352
353 is_repeated = field_descriptor.is_repeated
354 is_map_entry = _IsMapField(field_descriptor)
355 helper_decoders = {}
356
357 def AddDecoder(is_packed):
358 decode_type = field_descriptor.type
359 if (
360 decode_type == _FieldDescriptor.TYPE_ENUM
361 and not field_descriptor.enum_type.is_closed
362 ):
363 decode_type = _FieldDescriptor.TYPE_INT32
364
365 oneof_descriptor = None
366 if field_descriptor.containing_oneof is not None:
367 oneof_descriptor = field_descriptor
368
369 if is_map_entry:
370 is_message_map = _IsMessageMapField(field_descriptor)
371
372 field_decoder = decoder.MapDecoder(
373 field_descriptor,
374 _GetInitializeDefaultForMap(field_descriptor),
375 is_message_map,
376 )
377 elif decode_type == _FieldDescriptor.TYPE_STRING:
378 field_decoder = decoder.StringDecoder(
379 field_descriptor.number,
380 is_repeated,
381 is_packed,
382 field_descriptor,
383 field_descriptor._default_constructor,
384 not field_descriptor.has_presence,
385 )
386 elif field_descriptor.cpp_type == _FieldDescriptor.CPPTYPE_MESSAGE:
387 field_decoder = type_checkers.TYPE_TO_DECODER[decode_type](
388 field_descriptor.number,
389 is_repeated,
390 is_packed,
391 field_descriptor,
392 field_descriptor._default_constructor,
393 )
394 else:
395 field_decoder = type_checkers.TYPE_TO_DECODER[decode_type](
396 field_descriptor.number,
397 is_repeated,
398 is_packed,
399 # pylint: disable=protected-access
400 field_descriptor,
401 field_descriptor._default_constructor,
402 not field_descriptor.has_presence,
403 )
404
405 helper_decoders[is_packed] = field_decoder
406
407 AddDecoder(False)
408
409 if is_repeated and wire_format.IsTypePackable(field_descriptor.type):
410 # To support wire compatibility of adding packed = true, add a decoder for
411 # packed values regardless of the field's options.
412 AddDecoder(True)
413
414 field_descriptor._decoders = helper_decoders
415
416
417def _AddClassAttributesForNestedExtensions(descriptor, dictionary):
418 extensions = descriptor.extensions_by_name
419 for extension_name, extension_field in extensions.items():
420 assert extension_name not in dictionary
421 dictionary[extension_name] = extension_field
422
423
424def _AddEnumValues(descriptor, cls):
425 """Sets class-level attributes for all enum fields defined in this message.
426
427 Also exporting a class-level object that can name enum values.
428
429 Args:
430 descriptor: Descriptor object for this message type.
431 cls: Class we're constructing for this message type.
432 """
433 for enum_type in descriptor.enum_types:
434 setattr(cls, enum_type.name, enum_type_wrapper.EnumTypeWrapper(enum_type))
435 for enum_value in enum_type.values:
436 setattr(cls, enum_value.name, enum_value.number)
437
438
439def _GetInitializeDefaultForMap(field):
440 if not field.is_repeated:
441 raise ValueError('map_entry set on non-repeated field %s' % (field.name))
442 fields_by_name = field.message_type.fields_by_name
443 key_checker = type_checkers.GetTypeChecker(fields_by_name['key'])
444
445 value_field = fields_by_name['value']
446 if _IsMessageMapField(field):
447
448 def MakeMessageMapDefault(message):
449 return containers.MessageMap(
450 message._listener_for_children,
451 value_field.message_type,
452 key_checker,
453 field.message_type,
454 )
455
456 return MakeMessageMapDefault
457 else:
458 value_checker = type_checkers.GetTypeChecker(value_field)
459
460 def MakePrimitiveMapDefault(message):
461 return containers.ScalarMap(
462 message._listener_for_children,
463 key_checker,
464 value_checker,
465 field.message_type,
466 )
467
468 return MakePrimitiveMapDefault
469
470
471def _DefaultValueConstructorForField(field):
472 """Returns a function which returns a default value for a field.
473
474 Args:
475 field: FieldDescriptor object for this field.
476
477 The returned function has one argument:
478 message: Message instance containing this field, or a weakref proxy
479 of same.
480
481 That function in turn returns a default value for this field. The default
482 value may refer back to |message| via a weak reference.
483 """
484
485 if _IsMapField(field):
486 return _GetInitializeDefaultForMap(field)
487
488 if field.is_repeated:
489 if field.has_default_value and field.default_value != []:
490 raise ValueError(
491 'Repeated field default value not empty list: %s'
492 % (field.default_value)
493 )
494 if field.cpp_type == _FieldDescriptor.CPPTYPE_MESSAGE:
495 # We can't look at _concrete_class yet since it might not have
496 # been set. (Depends on order in which we initialize the classes).
497 message_type = field.message_type
498
499 def MakeRepeatedMessageDefault(message):
500 return containers.RepeatedCompositeFieldContainer(
501 message._listener_for_children, field.message_type
502 )
503
504 return MakeRepeatedMessageDefault
505 else:
506 type_checker = type_checkers.GetTypeChecker(field)
507
508 def MakeRepeatedScalarDefault(message):
509 return containers.RepeatedScalarFieldContainer(
510 message._listener_for_children, type_checker, field
511 )
512
513 return MakeRepeatedScalarDefault
514
515 if field.cpp_type == _FieldDescriptor.CPPTYPE_MESSAGE:
516 message_type = field.message_type
517
518 def MakeSubMessageDefault(message):
519 # _concrete_class may not yet be initialized.
520 if not hasattr(message_type, '_concrete_class'):
521 from google.protobuf import message_factory
522
523 message_factory.GetMessageClass(message_type)
524 result = message_type._concrete_class()
525 result._SetListener(
526 _OneofListener(message, field)
527 if field.containing_oneof is not None
528 else message._listener_for_children
529 )
530 return result
531
532 return MakeSubMessageDefault
533
534 def MakeScalarDefault(message):
535 # TODO: This may be broken since there may not be
536 # default_value. Combine with has_default_value somehow.
537 return field.default_value
538
539 return MakeScalarDefault
540
541
542def _ReraiseTypeErrorWithFieldName(message_name, field_name):
543 """Re-raise the currently-handled TypeError with the field name added."""
544 exc = sys.exc_info()[1]
545 if len(exc.args) == 1 and type(exc) is TypeError:
546 # simple TypeError; add field name to exception message
547 exc = TypeError('%s for field %s.%s' % (str(exc), message_name, field_name))
548
549 # re-raise possibly-amended exception with original traceback:
550 raise exc.with_traceback(sys.exc_info()[2])
551
552
553def _AddInitMethod(message_descriptor, cls):
554 """Adds an __init__ method to cls."""
555
556 def _GetIntegerEnumValue(enum_type, value):
557 """Convert a string or integer enum value to an integer.
558
559 If the value is a string, it is converted to the enum value in
560 enum_type with the same name. If the value is not a string, it's
561 returned as-is. (No conversion or bounds-checking is done.)
562 """
563 if isinstance(value, str):
564 try:
565 return enum_type.values_by_name[value].number
566 except KeyError:
567 raise ValueError(
568 'Enum type %s: unknown label "%s"' % (enum_type.full_name, value)
569 )
570 return value
571
572 def init(self, **kwargs):
573
574 def init_wkt_or_merge(field, msg, value):
575 if isinstance(value, message_mod.Message):
576 msg.MergeFrom(value)
577 elif (
578 isinstance(value, dict)
579 and field.message_type.full_name == _StructFullTypeName
580 ):
581 msg.Clear()
582 if len(value) == 1 and 'fields' in value:
583 try:
584 msg.update(value)
585 except:
586 msg.Clear()
587 msg.__init__(**value)
588 else:
589 msg.update(value)
590 elif hasattr(msg, '_internal_assign'):
591 msg._internal_assign(value)
592 else:
593 raise TypeError(
594 'Message field {0}.{1} must be initialized with a '
595 'dict or instance of same class, got {2}.'.format(
596 message_descriptor.name,
597 field.name,
598 type(value).__name__,
599 )
600 )
601
602 self._cached_byte_size = 0
603 self._cached_byte_size_dirty = len(kwargs) > 0
604 self._fields = {}
605 # Contains a mapping from oneof field descriptors to the descriptor
606 # of the currently set field in that oneof field.
607 self._oneofs = {}
608
609 # _unknown_fields is () when empty for efficiency, and will be turned into
610 # a list if fields are added.
611 self._unknown_fields = ()
612 self._is_present_in_parent = False
613 self._listener = message_listener_mod.NullMessageListener()
614 self._listener_for_children = _Listener(self)
615 self._frozen = False
616 for field_name, field_value in kwargs.items():
617 field = _GetFieldByName(message_descriptor, field_name)
618 if field is None:
619 raise TypeError(
620 '%s() got an unexpected keyword argument "%s"'
621 % (message_descriptor.name, field_name)
622 )
623 if field_value is None:
624 # field=None is the same as no field at all.
625 continue
626 if field.is_repeated:
627 field_copy = field._default_constructor(self)
628 if field.cpp_type == _FieldDescriptor.CPPTYPE_MESSAGE: # Composite
629 if _IsMapField(field):
630 if _IsMessageMapField(field):
631 for key in field_value:
632 item_value = field_value[key]
633 if isinstance(item_value, dict):
634 field_copy[key].__init__(**item_value)
635 else:
636 field_copy[key].MergeFrom(item_value)
637 else:
638 field_copy.update(field_value)
639 else:
640 for val in field_value:
641 if isinstance(val, dict) and (
642 field.message_type.full_name != _StructFullTypeName
643 ):
644 field_copy.add(**val)
645 else:
646 new_msg = field_copy.add()
647 init_wkt_or_merge(field, new_msg, val)
648 else: # Scalar
649 if field.cpp_type == _FieldDescriptor.CPPTYPE_ENUM:
650 field_value = [
651 _GetIntegerEnumValue(field.enum_type, val)
652 for val in field_value
653 ]
654 field_copy.extend(field_value)
655 self._fields[field] = field_copy
656 elif field.cpp_type == _FieldDescriptor.CPPTYPE_MESSAGE:
657 field_copy = field._default_constructor(self)
658 if isinstance(field_value, dict) and (
659 field.message_type.full_name != _StructFullTypeName
660 ):
661 new_val = field.message_type._concrete_class(**field_value)
662 field_copy.MergeFrom(new_val)
663 else:
664 try:
665 init_wkt_or_merge(field, field_copy, field_value)
666 except TypeError:
667 _ReraiseTypeErrorWithFieldName(message_descriptor.name, field_name)
668 self._fields[field] = field_copy
669 else:
670 if field.cpp_type == _FieldDescriptor.CPPTYPE_ENUM:
671 field_value = _GetIntegerEnumValue(field.enum_type, field_value)
672 try:
673 setattr(self, field_name, field_value)
674 except TypeError:
675 _ReraiseTypeErrorWithFieldName(message_descriptor.name, field_name)
676
677 init.__module__ = None
678 init.__doc__ = None
679 cls.__init__ = init
680
681
682def _GetFieldByName(message_descriptor, field_name):
683 """Returns a field descriptor by field name.
684
685 Args:
686 message_descriptor: A Descriptor describing all fields in message.
687 field_name: The name of the field to retrieve.
688
689 Returns:
690 The field descriptor associated with the field name.
691 """
692 try:
693 return message_descriptor.fields_by_name[field_name]
694 except KeyError:
695 raise ValueError(
696 'Protocol message %s has no "%s" field.'
697 % (message_descriptor.name, field_name)
698 )
699
700
701def _AddPropertiesForFields(descriptor, cls):
702 """Adds properties for all fields in this protocol message type."""
703 for field in descriptor.fields:
704 _AddPropertiesForField(field, cls)
705
706 if descriptor.is_extendable:
707 # _ExtensionDict is just an adaptor with no state so we allocate a new one
708 # every time it is accessed.
709 cls.Extensions = property(lambda self: _ExtensionDict(self))
710
711
712def _AddPropertiesForField(field, cls):
713 """Adds a public property for a protocol message field.
714
715 Clients can use this property to get and (in the case of non-repeated scalar
716 fields) directly set the value of a protocol message field.
717
718 Args:
719 field: A FieldDescriptor for this field.
720 cls: The class we're constructing.
721 """
722 # Catch it if we add other types that we should
723 # handle specially here.
724 assert _FieldDescriptor.MAX_CPPTYPE == 10
725
726 constant_name = field.name.upper() + '_FIELD_NUMBER'
727 setattr(cls, constant_name, field.number)
728
729 if field.is_repeated:
730 _AddPropertiesForRepeatedField(field, cls)
731 elif field.cpp_type == _FieldDescriptor.CPPTYPE_MESSAGE:
732 _AddPropertiesForNonRepeatedCompositeField(field, cls)
733 else:
734 _AddPropertiesForNonRepeatedScalarField(field, cls)
735
736
737class _FieldProperty(property):
738 __slots__ = ('DESCRIPTOR',)
739
740 def __init__(self, descriptor, getter, setter, doc):
741 property.__init__(self, getter, setter, doc=doc)
742 self.DESCRIPTOR = descriptor
743
744
745def _AddPropertiesForRepeatedField(field, cls):
746 """Adds a public property for a "repeated" protocol message field.
747
748 Clients can use this property to get the value of the field, which will be
749 either a RepeatedScalarFieldContainer or RepeatedCompositeFieldContainer (see
750 below).
751
752 Note that when clients add values to these containers, we perform
753 type-checking in the case of repeated scalar fields, and we also set any
754 necessary "has" bits as a side-effect.
755
756 Args:
757 field: A FieldDescriptor for this field.
758 cls: The class we're constructing.
759 """
760 proto_field_name = field.name
761 property_name = _PropertyName(proto_field_name)
762
763 def getter(self):
764 field_value = self._fields.get(field)
765 if field_value is None:
766 # Construct a new object to represent this field.
767 field_value = field._default_constructor(self)
768 if self._frozen:
769 field_value._SetFrozen()
770
771 # Atomically check if another thread has preempted us and, if not, swap
772 # in the new object we just created. If someone has preempted us, we
773 # take that object and discard ours.
774 # WARNING: We are relying on setdefault() being atomic. This is true
775 # in CPython but we haven't investigated others. This warning appears
776 # in several other locations in this file.
777 field_value = self._fields.setdefault(field, field_value)
778 return field_value
779
780 getter.__module__ = None
781 getter.__doc__ = 'Getter for %s.' % proto_field_name
782
783 # We define a setter just so we can throw an exception with a more
784 # helpful error message.
785 def setter(self, new_value):
786 raise AttributeError(
787 'Assignment not allowed to repeated field '
788 '"%s" in protocol message object.' % proto_field_name
789 )
790
791 doc = 'Magic attribute generated for "%s" proto field.' % proto_field_name
792 setattr(cls, property_name, _FieldProperty(field, getter, setter, doc=doc))
793
794
795def _AddPropertiesForNonRepeatedScalarField(field, cls):
796 """Adds a public property for a nonrepeated, scalar protocol message field.
797
798 Clients can use this property to get and directly set the value of the field.
799 Note that when the client sets the value of a field by using this property,
800 all necessary "has" bits are set as a side-effect, and we also perform
801 type-checking.
802
803 Args:
804 field: A FieldDescriptor for this field.
805 cls: The class we're constructing.
806 """
807 proto_field_name = field.name
808 property_name = _PropertyName(proto_field_name)
809 type_checker = type_checkers.GetTypeChecker(field)
810 default_value = field.default_value
811
812 def getter(self):
813 # TODO: This may be broken since there may not be
814 # default_value. Combine with has_default_value somehow.
815 return self._fields.get(field, default_value)
816
817 getter.__module__ = None
818 getter.__doc__ = 'Getter for %s.' % proto_field_name
819
820 def field_setter(self, new_value):
821 self._AssureWritable()
822 # pylint: disable=protected-access
823 # Testing the value for truthiness captures all of the implicit presence
824 # defaults (0, 0.0, enum 0, and False), except for -0.0.
825 try:
826 new_value = type_checker.CheckValue(new_value)
827 except TypeError as e:
828 raise TypeError(
829 'Cannot set %s to %.1024r: %s' % (field.full_name, new_value, e)
830 )
831 if not field.has_presence and decoder.IsDefaultScalarValue(new_value):
832 self._fields.pop(field, None)
833 else:
834 self._fields[field] = new_value
835 # Check _cached_byte_size_dirty inline to improve performance, since scalar
836 # setters are called frequently.
837 if not self._cached_byte_size_dirty:
838 self._Modified()
839
840 if field.containing_oneof:
841
842 def setter(self, new_value):
843 field_setter(self, new_value)
844 self._UpdateOneofState(field)
845
846 else:
847 setter = field_setter
848
849 setter.__module__ = None
850 setter.__doc__ = 'Setter for %s.' % proto_field_name
851
852 # Add a property to encapsulate the getter/setter.
853 doc = 'Magic attribute generated for "%s" proto field.' % proto_field_name
854 setattr(cls, property_name, _FieldProperty(field, getter, setter, doc=doc))
855
856
857def _AddPropertiesForNonRepeatedCompositeField(field, cls):
858 """Adds a public property for a nonrepeated, composite protocol message field.
859
860 A composite field is a "group" or "message" field.
861
862 Clients can use this property to get the value of the field, but cannot
863 assign to the property directly.
864
865 Args:
866 field: A FieldDescriptor for this field.
867 cls: The class we're constructing.
868 """
869 # TODO: Remove duplication with similar method
870 # for non-repeated scalars.
871 proto_field_name = field.name
872 property_name = _PropertyName(proto_field_name)
873
874 def getter(self):
875 field_value = self._fields.get(field)
876 if field_value is None:
877 # Construct a new object to represent this field.
878 field_value = field._default_constructor(self)
879 if self._frozen:
880 field_value._SetFrozen()
881
882 # Atomically check if another thread has preempted us and, if not, swap
883 # in the new object we just created. If someone has preempted us, we
884 # take that object and discard ours.
885 # WARNING: We are relying on setdefault() being atomic. This is true
886 # in CPython but we haven't investigated others. This warning appears
887 # in several other locations in this file.
888 field_value = self._fields.setdefault(field, field_value)
889 return field_value
890
891 getter.__module__ = None
892 getter.__doc__ = 'Getter for %s.' % proto_field_name
893
894 # We define a setter just so we can throw an exception with a more
895 # helpful error message.
896 def setter(self, new_value):
897 self._AssureWritable()
898 if field.message_type.full_name == 'google.protobuf.Timestamp':
899 getter(self)
900 self._fields[field].FromDatetime(new_value)
901 elif field.message_type.full_name == 'google.protobuf.Duration':
902 getter(self)
903 self._fields[field].FromTimedelta(new_value)
904 elif field.message_type.full_name == _StructFullTypeName:
905 getter(self)
906 self._fields[field].Clear()
907 self._fields[field].update(new_value)
908 elif field.message_type.full_name == _ListValueFullTypeName:
909 getter(self)
910 self._fields[field].Clear()
911 self._fields[field].extend(new_value)
912 else:
913 raise AttributeError(
914 'Assignment not allowed to composite field '
915 '"%s" in protocol message object.' % proto_field_name
916 )
917
918 # Add a property to encapsulate the getter.
919 doc = 'Magic attribute generated for "%s" proto field.' % proto_field_name
920 setattr(cls, property_name, _FieldProperty(field, getter, setter, doc=doc))
921
922
923def _AddPropertiesForExtensions(descriptor, cls):
924 """Adds properties for all fields in this protocol message type."""
925 extensions = descriptor.extensions_by_name
926 for extension_name, extension_field in extensions.items():
927 constant_name = extension_name.upper() + '_FIELD_NUMBER'
928 setattr(cls, constant_name, extension_field.number)
929
930 # TODO: Migrate all users of these attributes to functions like
931 # pool.FindExtensionByNumber(descriptor).
932 if descriptor.file is not None:
933 # TODO: Use cls.MESSAGE_FACTORY.pool when available.
934 pool = descriptor.file.pool
935
936
937def _AddStaticMethods(cls):
938
939 def RegisterExtension(_):
940 """no-op to keep generated code <=4.23 working with new runtimes."""
941 # This was originally removed in 5.26 (cl/595989309).
942 pass
943
944 cls.RegisterExtension = staticmethod(RegisterExtension)
945
946 def FromString(s):
947 message = cls()
948 message.MergeFromString(s)
949 return message
950
951 cls.FromString = staticmethod(FromString)
952
953
954def _IsPresent(item):
955 """Given a (FieldDescriptor, value) tuple from _fields, return true if the
956
957 value should be included in the list returned by ListFields().
958 """
959
960 if item[0].is_repeated:
961 return bool(item[1])
962 elif item[0].cpp_type == _FieldDescriptor.CPPTYPE_MESSAGE:
963 return item[1]._is_present_in_parent
964 else:
965 return True
966
967
968def _AddListFieldsMethod(message_descriptor, cls):
969 """Helper for _AddMessageMethods()."""
970
971 def ListFields(self):
972 all_fields = [item for item in self._fields.items() if _IsPresent(item)]
973 all_fields.sort(key=lambda item: item[0].number)
974 return all_fields
975
976 cls.ListFields = ListFields
977
978
979def _AddHasFieldMethod(message_descriptor, cls):
980 """Helper for _AddMessageMethods()."""
981
982 hassable_fields = {}
983 for field in message_descriptor.fields:
984 if field.is_repeated:
985 continue
986 # For proto3, only submessages and fields inside a oneof have presence.
987 if not field.has_presence:
988 continue
989 hassable_fields[field.name] = field
990
991 # Has methods are supported for oneof descriptors.
992 for oneof in message_descriptor.oneofs:
993 hassable_fields[oneof.name] = oneof
994
995 def HasField(self, field_name):
996 try:
997 field = hassable_fields[field_name]
998 except KeyError as exc:
999 raise ValueError(
1000 'Protocol message %s has no non-repeated field "%s" '
1001 'nor has presence is not available for this field.'
1002 % (message_descriptor.full_name, field_name)
1003 ) from exc
1004
1005 if isinstance(field, descriptor_mod.OneofDescriptor):
1006 try:
1007 return HasField(self, self._oneofs[field].name)
1008 except KeyError:
1009 return False
1010 else:
1011 if field.cpp_type == _FieldDescriptor.CPPTYPE_MESSAGE:
1012 value = self._fields.get(field)
1013 return value is not None and value._is_present_in_parent
1014 else:
1015 return field in self._fields
1016
1017 cls.HasField = HasField
1018
1019
1020def _AddClearFieldMethod(message_descriptor, cls):
1021 """Helper for _AddMessageMethods()."""
1022
1023 def ClearField(self, field_name):
1024 self._AssureWritable()
1025 try:
1026 field = message_descriptor.fields_by_name[field_name]
1027 except KeyError:
1028 try:
1029 field = message_descriptor.oneofs_by_name[field_name]
1030 if field in self._oneofs:
1031 field = self._oneofs[field]
1032 else:
1033 return
1034 except KeyError:
1035 raise ValueError(
1036 'Protocol message %s has no "%s" field.'
1037 % (message_descriptor.name, field_name)
1038 )
1039
1040 if field in self._fields:
1041 # To match the C++ implementation, we need to invalidate iterators
1042 # for map fields when ClearField() happens.
1043 if hasattr(self._fields[field], 'InvalidateIterators'):
1044 self._fields[field].InvalidateIterators()
1045
1046 # Note: If the field is a sub-message, its listener will still point
1047 # at us. That's fine, because the worst than can happen is that it
1048 # will call _Modified() and invalidate our byte size. Big deal.
1049 del self._fields[field]
1050
1051 if self._oneofs.get(field.containing_oneof, None) is field:
1052 del self._oneofs[field.containing_oneof]
1053
1054 # Always call _Modified() -- even if nothing was changed, this is
1055 # a mutating method, and thus calling it should cause the field to become
1056 # present in the parent message.
1057 self._Modified()
1058
1059 cls.ClearField = ClearField
1060
1061
1062def _AddClearExtensionMethod(cls):
1063 """Helper for _AddMessageMethods()."""
1064
1065 def ClearExtension(self, field_descriptor):
1066 self._AssureWritable()
1067 extension_dict._VerifyExtensionHandle(self, field_descriptor)
1068
1069 # Similar to ClearField(), above.
1070 if field_descriptor in self._fields:
1071 del self._fields[field_descriptor]
1072 self._Modified()
1073
1074 cls.ClearExtension = ClearExtension
1075
1076
1077def _AddHasExtensionMethod(cls):
1078 """Helper for _AddMessageMethods()."""
1079
1080 def HasExtension(self, field_descriptor):
1081 extension_dict._VerifyExtensionHandle(self, field_descriptor)
1082 if field_descriptor.is_repeated:
1083 raise KeyError('"%s" is repeated.' % field_descriptor.full_name)
1084
1085 if field_descriptor.cpp_type == _FieldDescriptor.CPPTYPE_MESSAGE:
1086 value = self._fields.get(field_descriptor)
1087 return value is not None and value._is_present_in_parent
1088 else:
1089 return field_descriptor in self._fields
1090
1091 cls.HasExtension = HasExtension
1092
1093
1094def _InternalUnpackAny(msg):
1095 """Unpacks Any message and returns the unpacked message.
1096
1097 This internal method is different from public Any Unpack method which takes
1098 the target message as argument. _InternalUnpackAny method does not have
1099 target message type and need to find the message type in descriptor pool.
1100
1101 Args:
1102 msg: An Any message to be unpacked.
1103
1104 Returns:
1105 The unpacked message.
1106 """
1107 # TODO: Don't use the factory of generated messages.
1108 # To make Any work with custom factories, use the message factory of the
1109 # parent message.
1110 # pylint: disable=g-import-not-at-top
1111 from google.protobuf import symbol_database
1112
1113 factory = symbol_database.Default()
1114
1115 type_url = msg.type_url
1116
1117 if not type_url:
1118 return None
1119
1120 # TODO: For now we just strip the hostname. Better logic will be
1121 # required.
1122 type_name = type_url.split('/')[-1]
1123 descriptor = factory.pool.FindMessageTypeByName(type_name)
1124
1125 if descriptor is None:
1126 return None
1127
1128 # Unable to import message_factory at top because of circular import.
1129 # pylint: disable=g-import-not-at-top
1130 from google.protobuf import message_factory
1131
1132 message_class = message_factory.GetMessageClass(descriptor)
1133 message = message_class()
1134
1135 message.ParseFromString(msg.value)
1136 return message
1137
1138
1139def _AddEqualsMethod(message_descriptor, cls):
1140 """Helper for _AddMessageMethods()."""
1141
1142 def __eq__(self, other):
1143 if self.DESCRIPTOR.full_name == _ListValueFullTypeName and isinstance(
1144 other, list
1145 ):
1146 return self._internal_compare(other)
1147 if self.DESCRIPTOR.full_name == _StructFullTypeName and isinstance(
1148 other, dict
1149 ):
1150 return self._internal_compare(other)
1151
1152 if (
1153 not isinstance(other, message_mod.Message)
1154 or other.DESCRIPTOR != self.DESCRIPTOR
1155 ):
1156 return NotImplemented
1157
1158 if self is other:
1159 return True
1160
1161 if self.DESCRIPTOR.full_name == _AnyFullTypeName:
1162 any_a = _InternalUnpackAny(self)
1163 any_b = _InternalUnpackAny(other)
1164 if any_a and any_b:
1165 return any_a == any_b
1166
1167 if not self.ListFields() == other.ListFields():
1168 return False
1169
1170 # TODO: Fix UnknownFieldSet to consider MessageSet extensions,
1171 # then use it for the comparison.
1172 unknown_fields = list(self._unknown_fields)
1173 unknown_fields.sort()
1174 other_unknown_fields = list(other._unknown_fields)
1175 other_unknown_fields.sort()
1176 return unknown_fields == other_unknown_fields
1177
1178 cls.__eq__ = __eq__
1179
1180
1181def _AddStrMethod(message_descriptor, cls):
1182 """Helper for _AddMessageMethods()."""
1183
1184 def __str__(self):
1185 return text_format.MessageToString(self)
1186
1187 cls.__str__ = __str__
1188
1189
1190def _AddReprMethod(message_descriptor, cls):
1191 """Helper for _AddMessageMethods()."""
1192
1193 def __repr__(self):
1194 return text_format.MessageToString(self)
1195
1196 cls.__repr__ = __repr__
1197
1198
1199def _AddUnicodeMethod(unused_message_descriptor, cls):
1200 """Helper for _AddMessageMethods()."""
1201
1202 def __unicode__(self):
1203 return text_format.MessageToString(self, as_utf8=True).decode('utf-8')
1204
1205 cls.__unicode__ = __unicode__
1206
1207
1208def _AddContainsMethod(message_descriptor, cls):
1209
1210 if message_descriptor.full_name == 'google.protobuf.Struct':
1211
1212 def __contains__(self, key):
1213 return key in self.fields
1214
1215 elif message_descriptor.full_name == 'google.protobuf.ListValue':
1216
1217 def __contains__(self, value):
1218 return value in self.items()
1219
1220 else:
1221
1222 def __contains__(self, field):
1223 return self.HasField(field)
1224
1225 cls.__contains__ = __contains__
1226
1227
1228def _BytesForNonRepeatedElement(value, field_number, field_type):
1229 """Returns the number of bytes needed to serialize a non-repeated element.
1230
1231 The returned byte count includes space for tag information and any other
1232 additional space associated with serializing value.
1233
1234 Args:
1235 value: Value we're serializing.
1236 field_number: Field number of this value. (Since the field number is stored
1237 as part of a varint-encoded tag, this has an impact on the total bytes
1238 required to serialize the value).
1239 field_type: The type of the field. One of the TYPE_* constants within
1240 FieldDescriptor.
1241 """
1242 try:
1243 fn = type_checkers.TYPE_TO_BYTE_SIZE_FN[field_type]
1244 return fn(field_number, value)
1245 except KeyError:
1246 raise message_mod.EncodeError('Unrecognized field type: %d' % field_type)
1247
1248
1249def _AddByteSizeMethod(message_descriptor, cls):
1250 """Helper for _AddMessageMethods()."""
1251
1252 def ByteSize(self):
1253 if not self._cached_byte_size_dirty:
1254 return self._cached_byte_size
1255
1256 size = 0
1257 descriptor = self.DESCRIPTOR
1258 if descriptor._is_map_entry:
1259 # Fields of map entry should always be serialized.
1260 key_field = descriptor.fields_by_name['key']
1261 _MaybeAddEncoder(cls, key_field)
1262 size = key_field._sizer(self.key)
1263 value_field = descriptor.fields_by_name['value']
1264 _MaybeAddEncoder(cls, value_field)
1265 size += value_field._sizer(self.value)
1266 else:
1267 for field_descriptor, field_value in self.ListFields():
1268 _MaybeAddEncoder(cls, field_descriptor)
1269 size += field_descriptor._sizer(field_value)
1270 for tag_bytes, value_bytes in self._unknown_fields:
1271 size += len(tag_bytes) + len(value_bytes)
1272
1273 self._cached_byte_size = size
1274 self._cached_byte_size_dirty = False
1275 self._listener_for_children.dirty = False
1276 return size
1277
1278 cls.ByteSize = ByteSize
1279
1280
1281def _AddSerializeToStringMethod(message_descriptor, cls):
1282 """Helper for _AddMessageMethods()."""
1283
1284 def SerializeToString(self, **kwargs):
1285 # Check if the message has all of its required fields set.
1286 if not self.IsInitialized():
1287 raise message_mod.EncodeError(
1288 'Message %s is missing required fields: %s'
1289 % (
1290 self.DESCRIPTOR.full_name,
1291 ','.join(self.FindInitializationErrors()),
1292 )
1293 )
1294 return self.SerializePartialToString(**kwargs)
1295
1296 cls.SerializeToString = SerializeToString
1297
1298
1299def _AddSerializePartialToStringMethod(message_descriptor, cls):
1300 """Helper for _AddMessageMethods()."""
1301
1302 def SerializePartialToString(self, **kwargs):
1303 out = BytesIO()
1304 self._InternalSerialize(out.write, **kwargs)
1305 return out.getvalue()
1306
1307 cls.SerializePartialToString = SerializePartialToString
1308
1309 def InternalSerialize(self, write_bytes, deterministic=None):
1310 if deterministic is None:
1311 deterministic = (
1312 api_implementation.IsPythonDefaultSerializationDeterministic()
1313 )
1314 else:
1315 deterministic = bool(deterministic)
1316
1317 descriptor = self.DESCRIPTOR
1318 if descriptor._is_map_entry:
1319 # Fields of map entry should always be serialized.
1320 key_field = descriptor.fields_by_name['key']
1321 _MaybeAddEncoder(cls, key_field)
1322 key_field._encoder(write_bytes, self.key, deterministic)
1323 value_field = descriptor.fields_by_name['value']
1324 _MaybeAddEncoder(cls, value_field)
1325 value_field._encoder(write_bytes, self.value, deterministic)
1326 else:
1327 for field_descriptor, field_value in self.ListFields():
1328 _MaybeAddEncoder(cls, field_descriptor)
1329 field_descriptor._encoder(write_bytes, field_value, deterministic)
1330 for tag_bytes, value_bytes in self._unknown_fields:
1331 write_bytes(tag_bytes)
1332 write_bytes(value_bytes)
1333
1334 cls._InternalSerialize = InternalSerialize
1335
1336
1337def _AddMergeFromStringMethod(message_descriptor, cls):
1338 """Helper for _AddMessageMethods()."""
1339
1340 def MergeFromString(self, serialized):
1341 self._AssureWritable()
1342 serialized = memoryview(serialized)
1343 length = len(serialized)
1344 try:
1345 if self._InternalParse(serialized, 0, length) != length:
1346 # The only reason _InternalParse would return early is if it
1347 # encountered an end-group tag.
1348 raise message_mod.DecodeError('Unexpected end-group tag.')
1349 except (IndexError, TypeError):
1350 # Now ord(buf[p:p+1]) == ord('') gets TypeError.
1351 raise message_mod.DecodeError('Truncated message.')
1352 except struct.error as e:
1353 raise message_mod.DecodeError(e)
1354 return length # Return this for legacy reasons.
1355
1356 cls.MergeFromString = MergeFromString
1357
1358 fields_by_tag = cls._fields_by_tag
1359 message_set_decoders_by_tag = cls._message_set_decoders_by_tag
1360
1361 def InternalParse(self, buffer, pos, end, current_depth=0):
1362 """Create a message from serialized bytes.
1363
1364 Args:
1365 self: Message, instance of the proto message object.
1366 buffer: memoryview of the serialized data.
1367 pos: int, position to start in the serialized data.
1368 end: int, end position of the serialized data.
1369
1370 Returns:
1371 Message object.
1372 """
1373 # Guard against internal misuse, since this function is called internally
1374 # quite extensively, and its easy to accidentally pass bytes.
1375 assert isinstance(buffer, memoryview)
1376 self._Modified()
1377 field_dict = self._fields
1378 while pos != end:
1379 tag_bytes, new_pos = decoder.ReadTag(buffer, pos)
1380 field_decoder, field_des = message_set_decoders_by_tag.get(
1381 tag_bytes, (None, None)
1382 )
1383 if field_decoder:
1384 pos = field_decoder(
1385 buffer, new_pos, end, self, field_dict, current_depth
1386 )
1387 continue
1388 field_des, is_packed = fields_by_tag.get(tag_bytes, (None, None))
1389 if field_des is None:
1390 if not self._unknown_fields: # pylint: disable=protected-access
1391 self._unknown_fields = [] # pylint: disable=protected-access
1392 field_number, wire_type = decoder.DecodeTag(tag_bytes)
1393 if field_number == 0:
1394 raise message_mod.DecodeError('Field number 0 is illegal.')
1395 data, new_pos = decoder._DecodeUnknownField(
1396 buffer, new_pos, end, field_number, wire_type
1397 ) # pylint: disable=protected-access
1398 if new_pos == -1:
1399 return pos
1400 self._unknown_fields.append(
1401 (tag_bytes, buffer[pos + len(tag_bytes) : new_pos].tobytes())
1402 )
1403 pos = new_pos
1404 else:
1405 _MaybeAddDecoder(cls, field_des)
1406 field_decoder = field_des._decoders[is_packed]
1407 pos = field_decoder(
1408 buffer, new_pos, end, self, field_dict, current_depth
1409 )
1410 if field_des.containing_oneof:
1411 self._UpdateOneofState(field_des)
1412 return pos
1413
1414 cls._InternalParse = InternalParse
1415
1416
1417def _AddIsInitializedMethod(message_descriptor, cls):
1418 """Adds the IsInitialized and FindInitializationError methods to the
1419
1420 protocol message class.
1421 """
1422
1423 required_fields = [
1424 field for field in message_descriptor.fields if field.is_required
1425 ]
1426
1427 def IsInitialized(self, errors=None):
1428 """Checks if all required fields of a message are set.
1429
1430 Args:
1431 errors: A list which, if provided, will be populated with the field paths
1432 of all missing required fields.
1433
1434 Returns:
1435 True iff the specified message has all required fields set.
1436 """
1437
1438 # Performance is critical so we avoid HasField() and ListFields().
1439
1440 for field in required_fields:
1441 if field not in self._fields or (
1442 field.cpp_type == _FieldDescriptor.CPPTYPE_MESSAGE
1443 and not self._fields[field]._is_present_in_parent
1444 ):
1445 if errors is not None:
1446 errors.extend(self.FindInitializationErrors())
1447 return False
1448
1449 for field, value in list(self._fields.items()): # dict can change size!
1450 if field.cpp_type == _FieldDescriptor.CPPTYPE_MESSAGE:
1451 if field.is_repeated:
1452 if field.message_type._is_map_entry:
1453 continue
1454 for element in value:
1455 if not element.IsInitialized():
1456 if errors is not None:
1457 errors.extend(self.FindInitializationErrors())
1458 return False
1459 elif value._is_present_in_parent and not value.IsInitialized():
1460 if errors is not None:
1461 errors.extend(self.FindInitializationErrors())
1462 return False
1463
1464 return True
1465
1466 cls.IsInitialized = IsInitialized
1467
1468 def FindInitializationErrors(self):
1469 """Finds required fields which are not initialized.
1470
1471 Returns:
1472 A list of strings. Each string is a path to an uninitialized field from
1473 the top-level message, e.g. "foo.bar[5].baz".
1474 """
1475
1476 errors = [] # simplify things
1477
1478 for field in required_fields:
1479 if not self.HasField(field.name):
1480 errors.append(field.name)
1481
1482 for field, value in self.ListFields():
1483 if field.cpp_type == _FieldDescriptor.CPPTYPE_MESSAGE:
1484 if field.is_extension:
1485 name = '(%s)' % field.full_name
1486 else:
1487 name = field.name
1488
1489 if _IsMapField(field):
1490 if _IsMessageMapField(field):
1491 for key in value:
1492 element = value[key]
1493 prefix = '%s[%s].' % (name, key)
1494 sub_errors = element.FindInitializationErrors()
1495 errors += [prefix + error for error in sub_errors]
1496 else:
1497 # ScalarMaps can't have any initialization errors.
1498 pass
1499 elif field.is_repeated:
1500 for i in range(len(value)):
1501 element = value[i]
1502 prefix = '%s[%d].' % (name, i)
1503 sub_errors = element.FindInitializationErrors()
1504 errors += [prefix + error for error in sub_errors]
1505 else:
1506 prefix = name + '.'
1507 sub_errors = value.FindInitializationErrors()
1508 errors += [prefix + error for error in sub_errors]
1509
1510 return errors
1511
1512 cls.FindInitializationErrors = FindInitializationErrors
1513
1514
1515def _FullyQualifiedClassName(klass):
1516 module = klass.__module__
1517 name = getattr(klass, '__qualname__', klass.__name__)
1518 if module in (None, 'builtins', '__builtin__'):
1519 return name
1520 return module + '.' + name
1521
1522
1523def _AddMergeFromMethod(cls):
1524 CPPTYPE_MESSAGE = _FieldDescriptor.CPPTYPE_MESSAGE
1525
1526 def MergeFrom(self, msg):
1527 self._AssureWritable()
1528 if not isinstance(msg, cls):
1529 raise TypeError(
1530 'Parameter to MergeFrom() must be instance of same class: '
1531 'expected %s got %s.'
1532 % (
1533 _FullyQualifiedClassName(cls),
1534 _FullyQualifiedClassName(msg.__class__),
1535 )
1536 )
1537
1538 assert msg is not self
1539 self._Modified()
1540
1541 fields = self._fields
1542
1543 for field, value in msg._fields.items():
1544 if field.is_repeated:
1545 field_value = fields.get(field)
1546 if field_value is None:
1547 # Construct a new object to represent this field.
1548 field_value = field._default_constructor(self)
1549 fields[field] = field_value
1550 field_value.MergeFrom(value)
1551 elif field.cpp_type == CPPTYPE_MESSAGE:
1552 if value._is_present_in_parent:
1553 field_value = fields.get(field)
1554 if field_value is None:
1555 # Construct a new object to represent this field.
1556 field_value = field._default_constructor(self)
1557 fields[field] = field_value
1558 field_value.MergeFrom(value)
1559 else:
1560 self._fields[field] = value
1561 if field.containing_oneof:
1562 self._UpdateOneofState(field)
1563
1564 if msg._unknown_fields:
1565 if not self._unknown_fields:
1566 self._unknown_fields = []
1567 self._unknown_fields.extend(msg._unknown_fields)
1568
1569 cls.MergeFrom = MergeFrom
1570
1571
1572def _AddWhichOneofMethod(message_descriptor, cls):
1573
1574 def WhichOneof(self, oneof_name):
1575 """Returns the name of the currently set field inside a oneof, or None."""
1576 try:
1577 field = message_descriptor.oneofs_by_name[oneof_name]
1578 except KeyError:
1579 raise ValueError('Protocol message has no oneof "%s" field.' % oneof_name)
1580
1581 nested_field = self._oneofs.get(field, None)
1582 if nested_field is not None and self.HasField(nested_field.name):
1583 return nested_field.name
1584 else:
1585 return None
1586
1587 cls.WhichOneof = WhichOneof
1588
1589
1590def _Clear(self):
1591 self._AssureWritable()
1592 # Clear fields.
1593 self._fields = {}
1594 self._unknown_fields = ()
1595
1596 self._oneofs = {}
1597 self._Modified()
1598
1599
1600def _SetFrozen(self):
1601 self._frozen = True
1602 for value in self._fields.values():
1603 if hasattr(value, '_SetFrozen'):
1604 value._SetFrozen()
1605
1606
1607def _AssureWritable(self):
1608 if self._frozen:
1609 warnings.warn(
1610 'Mutating messages or containers returned by GetOptions() is'
1611 ' deprecated and will raise an exception in a future release.',
1612 category=FutureWarning,
1613 stacklevel=3,
1614 )
1615 return self
1616
1617
1618def _UnknownFields(self):
1619 raise NotImplementedError(
1620 'Please use the add-on feaure '
1621 'unknown_fields.UnknownFieldSet(message) in '
1622 'unknown_fields.py instead.'
1623 )
1624
1625
1626def _DiscardUnknownFields(self):
1627 self._AssureWritable()
1628 self._unknown_fields = []
1629 for field, value in self.ListFields():
1630 if field.cpp_type == _FieldDescriptor.CPPTYPE_MESSAGE:
1631 if _IsMapField(field):
1632 if _IsMessageMapField(field):
1633 for key in value:
1634 value[key].DiscardUnknownFields()
1635 elif field.is_repeated:
1636 for sub_message in value:
1637 sub_message.DiscardUnknownFields()
1638 else:
1639 value.DiscardUnknownFields()
1640
1641
1642def _SetListener(self, listener):
1643 if listener is None:
1644 self._listener = message_listener_mod.NullMessageListener()
1645 else:
1646 self._listener = listener
1647
1648
1649def _AddMessageMethods(message_descriptor, cls):
1650 """Adds implementations of all Message methods to cls."""
1651 _AddListFieldsMethod(message_descriptor, cls)
1652 _AddHasFieldMethod(message_descriptor, cls)
1653 _AddClearFieldMethod(message_descriptor, cls)
1654 if message_descriptor.is_extendable:
1655 _AddClearExtensionMethod(cls)
1656 _AddHasExtensionMethod(cls)
1657 _AddEqualsMethod(message_descriptor, cls)
1658 _AddStrMethod(message_descriptor, cls)
1659 _AddReprMethod(message_descriptor, cls)
1660 _AddUnicodeMethod(message_descriptor, cls)
1661 _AddContainsMethod(message_descriptor, cls)
1662 _AddByteSizeMethod(message_descriptor, cls)
1663 _AddSerializeToStringMethod(message_descriptor, cls)
1664 _AddSerializePartialToStringMethod(message_descriptor, cls)
1665 _AddMergeFromStringMethod(message_descriptor, cls)
1666 _AddIsInitializedMethod(message_descriptor, cls)
1667 _AddMergeFromMethod(cls)
1668 _AddWhichOneofMethod(message_descriptor, cls)
1669 # Adds methods which do not depend on cls.
1670 cls.Clear = _Clear
1671 cls.DiscardUnknownFields = _DiscardUnknownFields
1672 cls._SetListener = _SetListener
1673 cls._SetFrozen = _SetFrozen
1674 cls._AssureWritable = _AssureWritable
1675
1676
1677def _AddPrivateHelperMethods(message_descriptor, cls):
1678 """Adds implementation of private helper methods to cls."""
1679
1680 def Modified(self):
1681 """Sets the _cached_byte_size_dirty bit to true,
1682
1683 and propagates this to our listener iff this was a state change.
1684 """
1685
1686 # Note: Some callers check _cached_byte_size_dirty before calling
1687 # _Modified() as an extra optimization. So, if this method is ever
1688 # changed such that it does stuff even when _cached_byte_size_dirty is
1689 # already true, the callers need to be updated.
1690 if not self._cached_byte_size_dirty:
1691 self._cached_byte_size_dirty = True
1692 self._listener_for_children.dirty = True
1693 self._is_present_in_parent = True
1694 self._listener.Modified()
1695
1696 def _UpdateOneofState(self, field):
1697 """Sets field as the active field in its containing oneof.
1698
1699 Will also delete currently active field in the oneof, if it is different
1700 from the argument. Does not mark the message as modified.
1701 """
1702 other_field = self._oneofs.setdefault(field.containing_oneof, field)
1703 if other_field is not field:
1704 del self._fields[other_field]
1705 self._oneofs[field.containing_oneof] = field
1706
1707 cls._Modified = Modified
1708 cls.SetInParent = Modified
1709 cls._UpdateOneofState = _UpdateOneofState
1710
1711
1712class _Listener(object):
1713 """MessageListener implementation that a parent message registers with its
1714
1715 child message.
1716
1717 In order to support semantics like:
1718
1719 foo.bar.baz.moo = 23
1720 assert foo.HasField('bar')
1721
1722 ...child objects must have back references to their parents.
1723 This helper class is at the heart of this support.
1724 """
1725
1726 def __init__(self, parent_message):
1727 """Args:
1728
1729 parent_message: The message whose _Modified() method we should call when
1730 we receive Modified() messages.
1731 """
1732 # This listener establishes a back reference from a child (contained) object
1733 # to its parent (containing) object. We make this a weak reference to avoid
1734 # creating cyclic garbage when the client finishes with the 'parent' object
1735 # in the tree.
1736 if isinstance(parent_message, weakref.ProxyType):
1737 self._parent_message_weakref = parent_message
1738 else:
1739 self._parent_message_weakref = weakref.proxy(parent_message)
1740
1741 # As an optimization, we also indicate directly on the listener whether
1742 # or not the parent message is dirty. This way we can avoid traversing
1743 # up the tree in the common case.
1744 self.dirty = False
1745
1746 def Modified(self):
1747 if self.dirty:
1748 return
1749 try:
1750 # Propagate the signal to our parents iff this is the first field set.
1751 self._parent_message_weakref._Modified()
1752 except ReferenceError:
1753 # We can get here if a client has kept a reference to a child object,
1754 # and is now setting a field on it, but the child's parent has been
1755 # garbage-collected. This is not an error.
1756 pass
1757
1758
1759class _OneofListener(_Listener):
1760 """Special listener implementation for setting composite oneof fields."""
1761
1762 def __init__(self, parent_message, field):
1763 """Args:
1764
1765 parent_message: The message whose _Modified() method we should call when
1766 we receive Modified() messages.
1767 field: The descriptor of the field being set in the parent message.
1768 """
1769 super(_OneofListener, self).__init__(parent_message)
1770 self._field = field
1771
1772 def Modified(self):
1773 """Also updates the state of the containing oneof in the parent message."""
1774 try:
1775 self._parent_message_weakref._UpdateOneofState(self._field)
1776 super(_OneofListener, self).Modified()
1777 except ReferenceError:
1778 pass