mirror of
https://gerrit.googlesource.com/git-repo
synced 2024-12-23 07:16:21 +00:00
1654 lines
62 KiB
Python
1654 lines
62 KiB
Python
# Protocol Buffers - Google's data interchange format
|
|
# Copyright 2008 Google Inc. All rights reserved.
|
|
# http://code.google.com/p/protobuf/
|
|
#
|
|
# Redistribution and use in source and binary forms, with or without
|
|
# modification, are permitted provided that the following conditions are
|
|
# met:
|
|
#
|
|
# * Redistributions of source code must retain the above copyright
|
|
# notice, this list of conditions and the following disclaimer.
|
|
# * Redistributions in binary form must reproduce the above
|
|
# copyright notice, this list of conditions and the following disclaimer
|
|
# in the documentation and/or other materials provided with the
|
|
# distribution.
|
|
# * Neither the name of Google Inc. nor the names of its
|
|
# contributors may be used to endorse or promote products derived from
|
|
# this software without specific prior written permission.
|
|
#
|
|
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
|
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
|
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
|
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
|
|
# This code is meant to work on Python 2.4 and above only.
|
|
#
|
|
# TODO(robinson): Helpers for verbose, common checks like seeing if a
|
|
# descriptor's cpp_type is CPPTYPE_MESSAGE.
|
|
|
|
"""Contains a metaclass and helper functions used to create
|
|
protocol message classes from Descriptor objects at runtime.
|
|
|
|
Recall that a metaclass is the "type" of a class.
|
|
(A class is to a metaclass what an instance is to a class.)
|
|
|
|
In this case, we use the GeneratedProtocolMessageType metaclass
|
|
to inject all the useful functionality into the classes
|
|
output by the protocol compiler at compile-time.
|
|
|
|
The upshot of all this is that the real implementation
|
|
details for ALL pure-Python protocol buffers are *here in
|
|
this file*.
|
|
"""
|
|
|
|
__author__ = 'robinson@google.com (Will Robinson)'
|
|
|
|
import heapq
|
|
import threading
|
|
import weakref
|
|
# We use "as" to avoid name collisions with variables.
|
|
from froofle.protobuf.internal import decoder
|
|
from froofle.protobuf.internal import encoder
|
|
from froofle.protobuf.internal import message_listener as message_listener_mod
|
|
from froofle.protobuf.internal import type_checkers
|
|
from froofle.protobuf.internal import wire_format
|
|
from froofle.protobuf import descriptor as descriptor_mod
|
|
from froofle.protobuf import message as message_mod
|
|
|
|
_FieldDescriptor = descriptor_mod.FieldDescriptor
|
|
|
|
|
|
class GeneratedProtocolMessageType(type):
|
|
|
|
"""Metaclass for protocol message classes created at runtime from Descriptors.
|
|
|
|
We add implementations for all methods described in the Message class. We
|
|
also create properties to allow getting/setting all fields in the protocol
|
|
message. Finally, we create slots to prevent users from accidentally
|
|
"setting" nonexistent fields in the protocol message, which then wouldn't get
|
|
serialized / deserialized properly.
|
|
|
|
The protocol compiler currently uses this metaclass to create protocol
|
|
message classes at runtime. Clients can also manually create their own
|
|
classes at runtime, as in this example:
|
|
|
|
mydescriptor = Descriptor(.....)
|
|
class MyProtoClass(Message):
|
|
__metaclass__ = GeneratedProtocolMessageType
|
|
DESCRIPTOR = mydescriptor
|
|
myproto_instance = MyProtoClass()
|
|
myproto.foo_field = 23
|
|
...
|
|
"""
|
|
|
|
# Must be consistent with the protocol-compiler code in
|
|
# proto2/compiler/internal/generator.*.
|
|
_DESCRIPTOR_KEY = 'DESCRIPTOR'
|
|
|
|
def __new__(cls, name, bases, dictionary):
|
|
"""Custom allocation for runtime-generated class types.
|
|
|
|
We override __new__ because this is apparently the only place
|
|
where we can meaningfully set __slots__ on the class we're creating(?).
|
|
(The interplay between metaclasses and slots is not very well-documented).
|
|
|
|
Args:
|
|
name: Name of the class (ignored, but required by the
|
|
metaclass protocol).
|
|
bases: Base classes of the class we're constructing.
|
|
(Should be message.Message). We ignore this field, but
|
|
it's required by the metaclass protocol
|
|
dictionary: The class dictionary of the class we're
|
|
constructing. dictionary[_DESCRIPTOR_KEY] must contain
|
|
a Descriptor object describing this protocol message
|
|
type.
|
|
|
|
Returns:
|
|
Newly-allocated class.
|
|
"""
|
|
descriptor = dictionary[GeneratedProtocolMessageType._DESCRIPTOR_KEY]
|
|
_AddSlots(descriptor, dictionary)
|
|
_AddClassAttributesForNestedExtensions(descriptor, dictionary)
|
|
superclass = super(GeneratedProtocolMessageType, cls)
|
|
return superclass.__new__(cls, name, bases, dictionary)
|
|
|
|
def __init__(cls, name, bases, dictionary):
|
|
"""Here we perform the majority of our work on the class.
|
|
We add enum getters, an __init__ method, implementations
|
|
of all Message methods, and properties for all fields
|
|
in the protocol type.
|
|
|
|
Args:
|
|
name: Name of the class (ignored, but required by the
|
|
metaclass protocol).
|
|
bases: Base classes of the class we're constructing.
|
|
(Should be message.Message). We ignore this field, but
|
|
it's required by the metaclass protocol
|
|
dictionary: The class dictionary of the class we're
|
|
constructing. dictionary[_DESCRIPTOR_KEY] must contain
|
|
a Descriptor object describing this protocol message
|
|
type.
|
|
"""
|
|
descriptor = dictionary[GeneratedProtocolMessageType._DESCRIPTOR_KEY]
|
|
# We act as a "friend" class of the descriptor, setting
|
|
# its _concrete_class attribute the first time we use a
|
|
# given descriptor to initialize a concrete protocol message
|
|
# class.
|
|
concrete_class_attr_name = '_concrete_class'
|
|
if not hasattr(descriptor, concrete_class_attr_name):
|
|
setattr(descriptor, concrete_class_attr_name, cls)
|
|
cls._known_extensions = []
|
|
_AddEnumValues(descriptor, cls)
|
|
_AddInitMethod(descriptor, cls)
|
|
_AddPropertiesForFields(descriptor, cls)
|
|
_AddStaticMethods(cls)
|
|
_AddMessageMethods(descriptor, cls)
|
|
_AddPrivateHelperMethods(cls)
|
|
superclass = super(GeneratedProtocolMessageType, cls)
|
|
superclass.__init__(cls, name, bases, dictionary)
|
|
|
|
|
|
# Stateless helpers for GeneratedProtocolMessageType below.
|
|
# Outside clients should not access these directly.
|
|
#
|
|
# I opted not to make any of these methods on the metaclass, to make it more
|
|
# clear that I'm not really using any state there and to keep clients from
|
|
# thinking that they have direct access to these construction helpers.
|
|
|
|
|
|
def _PropertyName(proto_field_name):
|
|
"""Returns the name of the public property attribute which
|
|
clients can use to get and (in some cases) set the value
|
|
of a protocol message field.
|
|
|
|
Args:
|
|
proto_field_name: The protocol message field name, exactly
|
|
as it appears (or would appear) in a .proto file.
|
|
"""
|
|
# TODO(robinson): Escape Python keywords (e.g., yield), and test this support.
|
|
# nnorwitz makes my day by writing:
|
|
# """
|
|
# FYI. See the keyword module in the stdlib. This could be as simple as:
|
|
#
|
|
# if keyword.iskeyword(proto_field_name):
|
|
# return proto_field_name + "_"
|
|
# return proto_field_name
|
|
# """
|
|
return proto_field_name
|
|
|
|
|
|
def _ValueFieldName(proto_field_name):
|
|
"""Returns the name of the (internal) instance attribute which objects
|
|
should use to store the current value for a given protocol message field.
|
|
|
|
Args:
|
|
proto_field_name: The protocol message field name, exactly
|
|
as it appears (or would appear) in a .proto file.
|
|
"""
|
|
return '_value_' + proto_field_name
|
|
|
|
|
|
def _HasFieldName(proto_field_name):
|
|
"""Returns the name of the (internal) instance attribute which
|
|
objects should use to store a boolean telling whether this field
|
|
is explicitly set or not.
|
|
|
|
Args:
|
|
proto_field_name: The protocol message field name, exactly
|
|
as it appears (or would appear) in a .proto file.
|
|
"""
|
|
return '_has_' + proto_field_name
|
|
|
|
|
|
def _AddSlots(message_descriptor, dictionary):
|
|
"""Adds a __slots__ entry to dictionary, containing the names of all valid
|
|
attributes for this message type.
|
|
|
|
Args:
|
|
message_descriptor: A Descriptor instance describing this message type.
|
|
dictionary: Class dictionary to which we'll add a '__slots__' entry.
|
|
"""
|
|
field_names = [_ValueFieldName(f.name) for f in message_descriptor.fields]
|
|
field_names.extend(_HasFieldName(f.name) for f in message_descriptor.fields
|
|
if f.label != _FieldDescriptor.LABEL_REPEATED)
|
|
field_names.extend(('Extensions',
|
|
'_cached_byte_size',
|
|
'_cached_byte_size_dirty',
|
|
'_called_transition_to_nonempty',
|
|
'_listener',
|
|
'_lock', '__weakref__'))
|
|
dictionary['__slots__'] = field_names
|
|
|
|
|
|
def _AddClassAttributesForNestedExtensions(descriptor, dictionary):
|
|
extension_dict = descriptor.extensions_by_name
|
|
for extension_name, extension_field in extension_dict.iteritems():
|
|
assert extension_name not in dictionary
|
|
dictionary[extension_name] = extension_field
|
|
|
|
|
|
def _AddEnumValues(descriptor, cls):
|
|
"""Sets class-level attributes for all enum fields defined in this message.
|
|
|
|
Args:
|
|
descriptor: Descriptor object for this message type.
|
|
cls: Class we're constructing for this message type.
|
|
"""
|
|
for enum_type in descriptor.enum_types:
|
|
for enum_value in enum_type.values:
|
|
setattr(cls, enum_value.name, enum_value.number)
|
|
|
|
|
|
def _DefaultValueForField(message, field):
|
|
"""Returns a default value for a field.
|
|
|
|
Args:
|
|
message: Message instance containing this field, or a weakref proxy
|
|
of same.
|
|
field: FieldDescriptor object for this field.
|
|
|
|
Returns: A default value for this field. May refer back to |message|
|
|
via a weak reference.
|
|
"""
|
|
# TODO(robinson): Only the repeated fields need a reference to 'message' (so
|
|
# that they can set the 'has' bit on the containing Message when someone
|
|
# append()s a value). We could special-case this, and avoid an extra
|
|
# function call on __init__() and Clear() for non-repeated fields.
|
|
|
|
# TODO(robinson): Find a better place for the default value assertion in this
|
|
# function. No need to repeat them every time the client calls Clear('foo').
|
|
# (We should probably just assert these things once and as early as possible,
|
|
# by tightening checking in the descriptor classes.)
|
|
if field.label == _FieldDescriptor.LABEL_REPEATED:
|
|
if field.default_value != []:
|
|
raise ValueError('Repeated field default value not empty list: %s' % (
|
|
field.default_value))
|
|
listener = _Listener(message, None)
|
|
if field.cpp_type == _FieldDescriptor.CPPTYPE_MESSAGE:
|
|
# We can't look at _concrete_class yet since it might not have
|
|
# been set. (Depends on order in which we initialize the classes).
|
|
return _RepeatedCompositeFieldContainer(listener, field.message_type)
|
|
else:
|
|
return _RepeatedScalarFieldContainer(
|
|
listener, type_checkers.GetTypeChecker(field.cpp_type, field.type))
|
|
|
|
if field.cpp_type == _FieldDescriptor.CPPTYPE_MESSAGE:
|
|
assert field.default_value is None
|
|
|
|
return field.default_value
|
|
|
|
|
|
def _AddInitMethod(message_descriptor, cls):
|
|
"""Adds an __init__ method to cls."""
|
|
fields = message_descriptor.fields
|
|
def init(self):
|
|
self._cached_byte_size = 0
|
|
self._cached_byte_size_dirty = False
|
|
self._listener = message_listener_mod.NullMessageListener()
|
|
self._called_transition_to_nonempty = False
|
|
# TODO(robinson): We should only create a lock if we really need one
|
|
# in this class.
|
|
self._lock = threading.Lock()
|
|
for field in fields:
|
|
default_value = _DefaultValueForField(self, field)
|
|
python_field_name = _ValueFieldName(field.name)
|
|
setattr(self, python_field_name, default_value)
|
|
if field.label != _FieldDescriptor.LABEL_REPEATED:
|
|
setattr(self, _HasFieldName(field.name), False)
|
|
self.Extensions = _ExtensionDict(self, cls._known_extensions)
|
|
|
|
init.__module__ = None
|
|
init.__doc__ = None
|
|
cls.__init__ = init
|
|
|
|
|
|
def _AddPropertiesForFields(descriptor, cls):
|
|
"""Adds properties for all fields in this protocol message type."""
|
|
for field in descriptor.fields:
|
|
_AddPropertiesForField(field, cls)
|
|
|
|
|
|
def _AddPropertiesForField(field, cls):
|
|
"""Adds a public property for a protocol message field.
|
|
Clients can use this property to get and (in the case
|
|
of non-repeated scalar fields) directly set the value
|
|
of a protocol message field.
|
|
|
|
Args:
|
|
field: A FieldDescriptor for this field.
|
|
cls: The class we're constructing.
|
|
"""
|
|
# Catch it if we add other types that we should
|
|
# handle specially here.
|
|
assert _FieldDescriptor.MAX_CPPTYPE == 10
|
|
|
|
if field.label == _FieldDescriptor.LABEL_REPEATED:
|
|
_AddPropertiesForRepeatedField(field, cls)
|
|
elif field.cpp_type == _FieldDescriptor.CPPTYPE_MESSAGE:
|
|
_AddPropertiesForNonRepeatedCompositeField(field, cls)
|
|
else:
|
|
_AddPropertiesForNonRepeatedScalarField(field, cls)
|
|
|
|
|
|
def _AddPropertiesForRepeatedField(field, cls):
|
|
"""Adds a public property for a "repeated" protocol message field. Clients
|
|
can use this property to get the value of the field, which will be either a
|
|
_RepeatedScalarFieldContainer or _RepeatedCompositeFieldContainer (see
|
|
below).
|
|
|
|
Note that when clients add values to these containers, we perform
|
|
type-checking in the case of repeated scalar fields, and we also set any
|
|
necessary "has" bits as a side-effect.
|
|
|
|
Args:
|
|
field: A FieldDescriptor for this field.
|
|
cls: The class we're constructing.
|
|
"""
|
|
proto_field_name = field.name
|
|
python_field_name = _ValueFieldName(proto_field_name)
|
|
property_name = _PropertyName(proto_field_name)
|
|
|
|
def getter(self):
|
|
return getattr(self, python_field_name)
|
|
getter.__module__ = None
|
|
getter.__doc__ = 'Getter for %s.' % proto_field_name
|
|
|
|
# We define a setter just so we can throw an exception with a more
|
|
# helpful error message.
|
|
def setter(self, new_value):
|
|
raise AttributeError('Assignment not allowed to repeated field '
|
|
'"%s" in protocol message object.' % proto_field_name)
|
|
|
|
doc = 'Magic attribute generated for "%s" proto field.' % proto_field_name
|
|
setattr(cls, property_name, property(getter, setter, doc=doc))
|
|
|
|
|
|
def _AddPropertiesForNonRepeatedScalarField(field, cls):
|
|
"""Adds a public property for a nonrepeated, scalar protocol message field.
|
|
Clients can use this property to get and directly set the value of the field.
|
|
Note that when the client sets the value of a field by using this property,
|
|
all necessary "has" bits are set as a side-effect, and we also perform
|
|
type-checking.
|
|
|
|
Args:
|
|
field: A FieldDescriptor for this field.
|
|
cls: The class we're constructing.
|
|
"""
|
|
proto_field_name = field.name
|
|
python_field_name = _ValueFieldName(proto_field_name)
|
|
has_field_name = _HasFieldName(proto_field_name)
|
|
property_name = _PropertyName(proto_field_name)
|
|
type_checker = type_checkers.GetTypeChecker(field.cpp_type, field.type)
|
|
|
|
def getter(self):
|
|
return getattr(self, python_field_name)
|
|
getter.__module__ = None
|
|
getter.__doc__ = 'Getter for %s.' % proto_field_name
|
|
def setter(self, new_value):
|
|
type_checker.CheckValue(new_value)
|
|
setattr(self, has_field_name, True)
|
|
self._MarkByteSizeDirty()
|
|
self._MaybeCallTransitionToNonemptyCallback()
|
|
setattr(self, python_field_name, new_value)
|
|
setter.__module__ = None
|
|
setter.__doc__ = 'Setter for %s.' % proto_field_name
|
|
|
|
# Add a property to encapsulate the getter/setter.
|
|
doc = 'Magic attribute generated for "%s" proto field.' % proto_field_name
|
|
setattr(cls, property_name, property(getter, setter, doc=doc))
|
|
|
|
|
|
def _AddPropertiesForNonRepeatedCompositeField(field, cls):
|
|
"""Adds a public property for a nonrepeated, composite protocol message field.
|
|
A composite field is a "group" or "message" field.
|
|
|
|
Clients can use this property to get the value of the field, but cannot
|
|
assign to the property directly.
|
|
|
|
Args:
|
|
field: A FieldDescriptor for this field.
|
|
cls: The class we're constructing.
|
|
"""
|
|
# TODO(robinson): Remove duplication with similar method
|
|
# for non-repeated scalars.
|
|
proto_field_name = field.name
|
|
python_field_name = _ValueFieldName(proto_field_name)
|
|
has_field_name = _HasFieldName(proto_field_name)
|
|
property_name = _PropertyName(proto_field_name)
|
|
message_type = field.message_type
|
|
|
|
def getter(self):
|
|
# TODO(robinson): Appropriately scary note about double-checked locking.
|
|
field_value = getattr(self, python_field_name)
|
|
if field_value is None:
|
|
self._lock.acquire()
|
|
try:
|
|
field_value = getattr(self, python_field_name)
|
|
if field_value is None:
|
|
field_class = message_type._concrete_class
|
|
field_value = field_class()
|
|
field_value._SetListener(_Listener(self, has_field_name))
|
|
setattr(self, python_field_name, field_value)
|
|
finally:
|
|
self._lock.release()
|
|
return field_value
|
|
getter.__module__ = None
|
|
getter.__doc__ = 'Getter for %s.' % proto_field_name
|
|
|
|
# We define a setter just so we can throw an exception with a more
|
|
# helpful error message.
|
|
def setter(self, new_value):
|
|
raise AttributeError('Assignment not allowed to composite field '
|
|
'"%s" in protocol message object.' % proto_field_name)
|
|
|
|
# Add a property to encapsulate the getter.
|
|
doc = 'Magic attribute generated for "%s" proto field.' % proto_field_name
|
|
setattr(cls, property_name, property(getter, setter, doc=doc))
|
|
|
|
|
|
def _AddStaticMethods(cls):
|
|
# TODO(robinson): This probably needs to be thread-safe(?)
|
|
def RegisterExtension(extension_handle):
|
|
extension_handle.containing_type = cls.DESCRIPTOR
|
|
cls._known_extensions.append(extension_handle)
|
|
cls.RegisterExtension = staticmethod(RegisterExtension)
|
|
|
|
|
|
def _AddListFieldsMethod(message_descriptor, cls):
|
|
"""Helper for _AddMessageMethods()."""
|
|
|
|
# Ensure that we always list in ascending field-number order.
|
|
# For non-extension fields, we can do the sort once, here, at import-time.
|
|
# For extensions, we sort on each ListFields() call, though
|
|
# we could do better if we have to.
|
|
fields = sorted(message_descriptor.fields, key=lambda f: f.number)
|
|
has_field_names = (_HasFieldName(f.name) for f in fields)
|
|
value_field_names = (_ValueFieldName(f.name) for f in fields)
|
|
triplets = zip(has_field_names, value_field_names, fields)
|
|
|
|
def ListFields(self):
|
|
# We need to list all extension and non-extension fields
|
|
# together, in sorted order by field number.
|
|
|
|
# Step 0: Get an iterator over all "set" non-extension fields,
|
|
# sorted by field number.
|
|
# This iterator yields (field_number, field_descriptor, value) tuples.
|
|
def SortedSetFieldsIter():
|
|
# Note that triplets is already sorted by field number.
|
|
for has_field_name, value_field_name, field_descriptor in triplets:
|
|
if field_descriptor.label == _FieldDescriptor.LABEL_REPEATED:
|
|
value = getattr(self, _ValueFieldName(field_descriptor.name))
|
|
if len(value) > 0:
|
|
yield (field_descriptor.number, field_descriptor, value)
|
|
elif getattr(self, _HasFieldName(field_descriptor.name)):
|
|
value = getattr(self, _ValueFieldName(field_descriptor.name))
|
|
yield (field_descriptor.number, field_descriptor, value)
|
|
sorted_fields = SortedSetFieldsIter()
|
|
|
|
# Step 1: Get an iterator over all "set" extension fields,
|
|
# sorted by field number.
|
|
# This iterator ALSO yields (field_number, field_descriptor, value) tuples.
|
|
# TODO(robinson): It's not necessary to repeat this with each
|
|
# serialization call. We can do better.
|
|
sorted_extension_fields = sorted(
|
|
[(f.number, f, v) for f, v in self.Extensions._ListSetExtensions()])
|
|
|
|
# Step 2: Create a composite iterator that merges the extension-
|
|
# and non-extension fields, and that still yields fields in
|
|
# sorted order.
|
|
all_set_fields = _ImergeSorted(sorted_fields, sorted_extension_fields)
|
|
|
|
# Step 3: Strip off the field numbers and return.
|
|
return [field[1:] for field in all_set_fields]
|
|
|
|
cls.ListFields = ListFields
|
|
|
|
def _AddHasFieldMethod(cls):
|
|
"""Helper for _AddMessageMethods()."""
|
|
def HasField(self, field_name):
|
|
try:
|
|
return getattr(self, _HasFieldName(field_name))
|
|
except AttributeError:
|
|
raise ValueError('Protocol message has no "%s" field.' % field_name)
|
|
cls.HasField = HasField
|
|
|
|
|
|
def _AddClearFieldMethod(cls):
|
|
"""Helper for _AddMessageMethods()."""
|
|
def ClearField(self, field_name):
|
|
try:
|
|
field = self.DESCRIPTOR.fields_by_name[field_name]
|
|
except KeyError:
|
|
raise ValueError('Protocol message has no "%s" field.' % field_name)
|
|
proto_field_name = field.name
|
|
python_field_name = _ValueFieldName(proto_field_name)
|
|
has_field_name = _HasFieldName(proto_field_name)
|
|
default_value = _DefaultValueForField(self, field)
|
|
if field.label == _FieldDescriptor.LABEL_REPEATED:
|
|
self._MarkByteSizeDirty()
|
|
else:
|
|
if field.cpp_type == _FieldDescriptor.CPPTYPE_MESSAGE:
|
|
old_field_value = getattr(self, python_field_name)
|
|
if old_field_value is not None:
|
|
# Snip the old object out of the object tree.
|
|
old_field_value._SetListener(None)
|
|
if getattr(self, has_field_name):
|
|
setattr(self, has_field_name, False)
|
|
# Set dirty bit on ourself and parents only if
|
|
# we're actually changing state.
|
|
self._MarkByteSizeDirty()
|
|
setattr(self, python_field_name, default_value)
|
|
cls.ClearField = ClearField
|
|
|
|
|
|
def _AddClearExtensionMethod(cls):
|
|
"""Helper for _AddMessageMethods()."""
|
|
def ClearExtension(self, extension_handle):
|
|
self.Extensions._ClearExtension(extension_handle)
|
|
cls.ClearExtension = ClearExtension
|
|
|
|
|
|
def _AddClearMethod(cls):
|
|
"""Helper for _AddMessageMethods()."""
|
|
def Clear(self):
|
|
# Clear fields.
|
|
fields = self.DESCRIPTOR.fields
|
|
for field in fields:
|
|
self.ClearField(field.name)
|
|
# Clear extensions.
|
|
extensions = self.Extensions._ListSetExtensions()
|
|
for extension in extensions:
|
|
self.ClearExtension(extension[0])
|
|
cls.Clear = Clear
|
|
|
|
|
|
def _AddHasExtensionMethod(cls):
|
|
"""Helper for _AddMessageMethods()."""
|
|
def HasExtension(self, extension_handle):
|
|
return self.Extensions._HasExtension(extension_handle)
|
|
cls.HasExtension = HasExtension
|
|
|
|
|
|
def _AddEqualsMethod(message_descriptor, cls):
|
|
"""Helper for _AddMessageMethods()."""
|
|
def __eq__(self, other):
|
|
if self is other:
|
|
return True
|
|
|
|
# Compare all fields contained directly in this message.
|
|
for field_descriptor in message_descriptor.fields:
|
|
label = field_descriptor.label
|
|
property_name = _PropertyName(field_descriptor.name)
|
|
# Non-repeated field equality requires matching "has" bits as well
|
|
# as having an equal value.
|
|
if label != _FieldDescriptor.LABEL_REPEATED:
|
|
self_has = self.HasField(property_name)
|
|
other_has = other.HasField(property_name)
|
|
if self_has != other_has:
|
|
return False
|
|
if not self_has:
|
|
# If the "has" bit for this field is False, we must stop here.
|
|
# Otherwise we will recurse forever on recursively-defined protos.
|
|
continue
|
|
if getattr(self, property_name) != getattr(other, property_name):
|
|
return False
|
|
|
|
# Compare the extensions present in both messages.
|
|
return self.Extensions == other.Extensions
|
|
cls.__eq__ = __eq__
|
|
|
|
|
|
def _AddSetListenerMethod(cls):
|
|
"""Helper for _AddMessageMethods()."""
|
|
def SetListener(self, listener):
|
|
if listener is None:
|
|
self._listener = message_listener_mod.NullMessageListener()
|
|
else:
|
|
self._listener = listener
|
|
cls._SetListener = SetListener
|
|
|
|
|
|
def _BytesForNonRepeatedElement(value, field_number, field_type):
|
|
"""Returns the number of bytes needed to serialize a non-repeated element.
|
|
The returned byte count includes space for tag information and any
|
|
other additional space associated with serializing value.
|
|
|
|
Args:
|
|
value: Value we're serializing.
|
|
field_number: Field number of this value. (Since the field number
|
|
is stored as part of a varint-encoded tag, this has an impact
|
|
on the total bytes required to serialize the value).
|
|
field_type: The type of the field. One of the TYPE_* constants
|
|
within FieldDescriptor.
|
|
"""
|
|
try:
|
|
fn = type_checkers.TYPE_TO_BYTE_SIZE_FN[field_type]
|
|
return fn(field_number, value)
|
|
except KeyError:
|
|
raise message_mod.EncodeError('Unrecognized field type: %d' % field_type)
|
|
|
|
|
|
def _AddByteSizeMethod(message_descriptor, cls):
|
|
"""Helper for _AddMessageMethods()."""
|
|
|
|
def BytesForField(message, field, value):
|
|
"""Returns the number of bytes required to serialize a single field
|
|
in message. The field may be repeated or not, composite or not.
|
|
|
|
Args:
|
|
message: The Message instance containing a field of the given type.
|
|
field: A FieldDescriptor describing the field of interest.
|
|
value: The value whose byte size we're interested in.
|
|
|
|
Returns: The number of bytes required to serialize the current value
|
|
of "field" in "message", including space for tags and any other
|
|
necessary information.
|
|
"""
|
|
|
|
if _MessageSetField(field):
|
|
return wire_format.MessageSetItemByteSize(field.number, value)
|
|
|
|
field_number, field_type = field.number, field.type
|
|
|
|
# Repeated fields.
|
|
if field.label == _FieldDescriptor.LABEL_REPEATED:
|
|
elements = value
|
|
else:
|
|
elements = [value]
|
|
|
|
size = sum(_BytesForNonRepeatedElement(element, field_number, field_type)
|
|
for element in elements)
|
|
return size
|
|
|
|
fields = message_descriptor.fields
|
|
has_field_names = (_HasFieldName(f.name) for f in fields)
|
|
zipped = zip(has_field_names, fields)
|
|
|
|
def ByteSize(self):
|
|
if not self._cached_byte_size_dirty:
|
|
return self._cached_byte_size
|
|
|
|
size = 0
|
|
# Hardcoded fields first.
|
|
for has_field_name, field in zipped:
|
|
if (field.label == _FieldDescriptor.LABEL_REPEATED
|
|
or getattr(self, has_field_name)):
|
|
value = getattr(self, _ValueFieldName(field.name))
|
|
size += BytesForField(self, field, value)
|
|
# Extensions next.
|
|
for field, value in self.Extensions._ListSetExtensions():
|
|
size += BytesForField(self, field, value)
|
|
|
|
self._cached_byte_size = size
|
|
self._cached_byte_size_dirty = False
|
|
return size
|
|
cls.ByteSize = ByteSize
|
|
|
|
|
|
def _MessageSetField(field_descriptor):
|
|
"""Checks if a field should be serialized using the message set wire format.
|
|
|
|
Args:
|
|
field_descriptor: Descriptor of the field.
|
|
|
|
Returns:
|
|
True if the field should be serialized using the message set wire format,
|
|
false otherwise.
|
|
"""
|
|
return (field_descriptor.is_extension and
|
|
field_descriptor.label != _FieldDescriptor.LABEL_REPEATED and
|
|
field_descriptor.cpp_type == _FieldDescriptor.CPPTYPE_MESSAGE and
|
|
field_descriptor.containing_type.GetOptions().message_set_wire_format)
|
|
|
|
|
|
def _SerializeValueToEncoder(value, field_number, field_descriptor, encoder):
|
|
"""Appends the serialization of a single value to encoder.
|
|
|
|
Args:
|
|
value: Value to serialize.
|
|
field_number: Field number of this value.
|
|
field_descriptor: Descriptor of the field to serialize.
|
|
encoder: encoder.Encoder object to which we should serialize this value.
|
|
"""
|
|
if _MessageSetField(field_descriptor):
|
|
encoder.AppendMessageSetItem(field_number, value)
|
|
return
|
|
|
|
try:
|
|
method = type_checkers.TYPE_TO_SERIALIZE_METHOD[field_descriptor.type]
|
|
method(encoder, field_number, value)
|
|
except KeyError:
|
|
raise message_mod.EncodeError('Unrecognized field type: %d' %
|
|
field_descriptor.type)
|
|
|
|
|
|
def _ImergeSorted(*streams):
|
|
"""Merges N sorted iterators into a single sorted iterator.
|
|
Each element in streams must be an iterable that yields
|
|
its elements in sorted order, and the elements contained
|
|
in each stream must all be comparable.
|
|
|
|
There may be repeated elements in the component streams or
|
|
across the streams; the repeated elements will all be repeated
|
|
in the merged iterator as well.
|
|
|
|
I believe that the heapq module at HEAD in the Python
|
|
sources has a method like this, but for now we roll our own.
|
|
"""
|
|
iters = [iter(stream) for stream in streams]
|
|
heap = []
|
|
for index, it in enumerate(iters):
|
|
try:
|
|
heap.append((it.next(), index))
|
|
except StopIteration:
|
|
pass
|
|
heapq.heapify(heap)
|
|
|
|
while heap:
|
|
smallest_value, idx = heap[0]
|
|
yield smallest_value
|
|
try:
|
|
next_element = iters[idx].next()
|
|
heapq.heapreplace(heap, (next_element, idx))
|
|
except StopIteration:
|
|
heapq.heappop(heap)
|
|
|
|
|
|
def _AddSerializeToStringMethod(message_descriptor, cls):
|
|
"""Helper for _AddMessageMethods()."""
|
|
|
|
def SerializeToString(self):
|
|
# Check if the message has all of its required fields set.
|
|
errors = []
|
|
if not _InternalIsInitialized(self, errors):
|
|
raise message_mod.EncodeError('\n'.join(errors))
|
|
return self.SerializePartialToString()
|
|
cls.SerializeToString = SerializeToString
|
|
|
|
|
|
def _AddSerializePartialToStringMethod(message_descriptor, cls):
|
|
"""Helper for _AddMessageMethods()."""
|
|
Encoder = encoder.Encoder
|
|
|
|
def SerializePartialToString(self):
|
|
encoder = Encoder()
|
|
# We need to serialize all extension and non-extension fields
|
|
# together, in sorted order by field number.
|
|
for field_descriptor, field_value in self.ListFields():
|
|
if field_descriptor.label == _FieldDescriptor.LABEL_REPEATED:
|
|
repeated_value = field_value
|
|
else:
|
|
repeated_value = [field_value]
|
|
for element in repeated_value:
|
|
_SerializeValueToEncoder(element, field_descriptor.number,
|
|
field_descriptor, encoder)
|
|
return encoder.ToString()
|
|
cls.SerializePartialToString = SerializePartialToString
|
|
|
|
|
|
def _WireTypeForFieldType(field_type):
|
|
"""Given a field type, returns the expected wire type."""
|
|
try:
|
|
return type_checkers.FIELD_TYPE_TO_WIRE_TYPE[field_type]
|
|
except KeyError:
|
|
raise message_mod.DecodeError('Unknown field type: %d' % field_type)
|
|
|
|
|
|
def _RecursivelyMerge(field_number, field_type, decoder, message):
|
|
"""Decodes a message from decoder into message.
|
|
message is either a group or a nested message within some containing
|
|
protocol message. If it's a group, we use the group protocol to
|
|
deserialize, and if it's a nested message, we use the nested-message
|
|
protocol.
|
|
|
|
Args:
|
|
field_number: The field number of message in its enclosing protocol buffer.
|
|
field_type: The field type of message. Must be either TYPE_MESSAGE
|
|
or TYPE_GROUP.
|
|
decoder: Decoder to read from.
|
|
message: Message to deserialize into.
|
|
"""
|
|
if field_type == _FieldDescriptor.TYPE_MESSAGE:
|
|
decoder.ReadMessageInto(message)
|
|
elif field_type == _FieldDescriptor.TYPE_GROUP:
|
|
decoder.ReadGroupInto(field_number, message)
|
|
else:
|
|
raise message_mod.DecodeError('Unexpected field type: %d' % field_type)
|
|
|
|
|
|
def _DeserializeScalarFromDecoder(field_type, decoder):
|
|
"""Deserializes a scalar of the requested type from decoder. field_type must
|
|
be a scalar (non-group, non-message) FieldDescriptor.FIELD_* constant.
|
|
"""
|
|
try:
|
|
method = type_checkers.TYPE_TO_DESERIALIZE_METHOD[field_type]
|
|
return method(decoder)
|
|
except KeyError:
|
|
raise message_mod.DecodeError('Unrecognized field type: %d' % field_type)
|
|
|
|
|
|
def _SkipField(field_number, wire_type, decoder):
|
|
"""Skips a field with the specified wire type.
|
|
|
|
Args:
|
|
field_number: Tag number of the field to skip.
|
|
wire_type: Wire type of the field to skip.
|
|
decoder: Decoder used to deserialize the messsage. It must be positioned
|
|
just after reading the the tag and wire type of the field.
|
|
"""
|
|
if wire_type == wire_format.WIRETYPE_VARINT:
|
|
decoder.ReadUInt64()
|
|
elif wire_type == wire_format.WIRETYPE_FIXED64:
|
|
decoder.ReadFixed64()
|
|
elif wire_type == wire_format.WIRETYPE_LENGTH_DELIMITED:
|
|
decoder.SkipBytes(decoder.ReadInt32())
|
|
elif wire_type == wire_format.WIRETYPE_START_GROUP:
|
|
_SkipGroup(field_number, decoder)
|
|
elif wire_type == wire_format.WIRETYPE_END_GROUP:
|
|
pass
|
|
elif wire_type == wire_format.WIRETYPE_FIXED32:
|
|
decoder.ReadFixed32()
|
|
else:
|
|
raise message_mod.DecodeError('Unexpected wire type: %d' % wire_type)
|
|
|
|
|
|
def _SkipGroup(group_number, decoder):
|
|
"""Skips a nested group from the decoder.
|
|
|
|
Args:
|
|
group_number: Tag number of the group to skip.
|
|
decoder: Decoder used to deserialize the message. It must be positioned
|
|
exactly at the beginning of the message that should be skipped.
|
|
"""
|
|
while True:
|
|
field_number, wire_type = decoder.ReadFieldNumberAndWireType()
|
|
if (wire_type == wire_format.WIRETYPE_END_GROUP and
|
|
field_number == group_number):
|
|
return
|
|
_SkipField(field_number, wire_type, decoder)
|
|
|
|
|
|
def _DeserializeMessageSetItem(message, decoder):
|
|
"""Deserializes a message using the message set wire format.
|
|
|
|
Args:
|
|
message: Message to be parsed to.
|
|
decoder: The decoder to be used to deserialize encoded data. Note that the
|
|
decoder should be positioned just after reading the START_GROUP tag that
|
|
began the messageset item.
|
|
"""
|
|
field_number, wire_type = decoder.ReadFieldNumberAndWireType()
|
|
if wire_type != wire_format.WIRETYPE_VARINT or field_number != 2:
|
|
raise message_mod.DecodeError(
|
|
'Incorrect message set wire format. '
|
|
'wire_type: %d, field_number: %d' % (wire_type, field_number))
|
|
|
|
type_id = decoder.ReadInt32()
|
|
field_number, wire_type = decoder.ReadFieldNumberAndWireType()
|
|
if wire_type != wire_format.WIRETYPE_LENGTH_DELIMITED or field_number != 3:
|
|
raise message_mod.DecodeError(
|
|
'Incorrect message set wire format. '
|
|
'wire_type: %d, field_number: %d' % (wire_type, field_number))
|
|
|
|
extension_dict = message.Extensions
|
|
extensions_by_number = extension_dict._AllExtensionsByNumber()
|
|
if type_id not in extensions_by_number:
|
|
_SkipField(field_number, wire_type, decoder)
|
|
return
|
|
|
|
field_descriptor = extensions_by_number[type_id]
|
|
value = extension_dict[field_descriptor]
|
|
decoder.ReadMessageInto(value)
|
|
# Read the END_GROUP tag.
|
|
field_number, wire_type = decoder.ReadFieldNumberAndWireType()
|
|
if wire_type != wire_format.WIRETYPE_END_GROUP or field_number != 1:
|
|
raise message_mod.DecodeError(
|
|
'Incorrect message set wire format. '
|
|
'wire_type: %d, field_number: %d' % (wire_type, field_number))
|
|
|
|
|
|
def _DeserializeOneEntity(message_descriptor, message, decoder):
|
|
"""Deserializes the next wire entity from decoder into message.
|
|
The next wire entity is either a scalar or a nested message,
|
|
and may also be an element in a repeated field (the wire encoding
|
|
is the same).
|
|
|
|
Args:
|
|
message_descriptor: A Descriptor instance describing all fields
|
|
in message.
|
|
message: The Message instance into which we're decoding our fields.
|
|
decoder: The Decoder we're using to deserialize encoded data.
|
|
|
|
Returns: The number of bytes read from decoder during this method.
|
|
"""
|
|
initial_position = decoder.Position()
|
|
field_number, wire_type = decoder.ReadFieldNumberAndWireType()
|
|
extension_dict = message.Extensions
|
|
extensions_by_number = extension_dict._AllExtensionsByNumber()
|
|
if field_number in message_descriptor.fields_by_number:
|
|
# Non-extension field.
|
|
field_descriptor = message_descriptor.fields_by_number[field_number]
|
|
value = getattr(message, _PropertyName(field_descriptor.name))
|
|
def nonextension_setter_fn(scalar):
|
|
setattr(message, _PropertyName(field_descriptor.name), scalar)
|
|
scalar_setter_fn = nonextension_setter_fn
|
|
elif field_number in extensions_by_number:
|
|
# Extension field.
|
|
field_descriptor = extensions_by_number[field_number]
|
|
value = extension_dict[field_descriptor]
|
|
def extension_setter_fn(scalar):
|
|
extension_dict[field_descriptor] = scalar
|
|
scalar_setter_fn = extension_setter_fn
|
|
elif wire_type == wire_format.WIRETYPE_END_GROUP:
|
|
# We assume we're being parsed as the group that's ended.
|
|
return 0
|
|
elif (wire_type == wire_format.WIRETYPE_START_GROUP and
|
|
field_number == 1 and
|
|
message_descriptor.GetOptions().message_set_wire_format):
|
|
# A Message Set item.
|
|
_DeserializeMessageSetItem(message, decoder)
|
|
return decoder.Position() - initial_position
|
|
else:
|
|
_SkipField(field_number, wire_type, decoder)
|
|
return decoder.Position() - initial_position
|
|
|
|
# If we reach this point, we've identified the field as either
|
|
# hardcoded or extension, and set |field_descriptor|, |scalar_setter_fn|,
|
|
# and |value| appropriately. Now actually deserialize the thing.
|
|
#
|
|
# field_descriptor: Describes the field we're deserializing.
|
|
# value: The value currently stored in the field to deserialize.
|
|
# Used only if the field is composite and/or repeated.
|
|
# scalar_setter_fn: A function F such that F(scalar) will
|
|
# set a nonrepeated scalar value for this field. Used only
|
|
# if this field is a nonrepeated scalar.
|
|
|
|
field_number = field_descriptor.number
|
|
field_type = field_descriptor.type
|
|
expected_wire_type = _WireTypeForFieldType(field_type)
|
|
if wire_type != expected_wire_type:
|
|
# Need to fill in uninterpreted_bytes. Work for the next CL.
|
|
raise RuntimeError('TODO(robinson): Wiretype mismatches not handled.')
|
|
|
|
property_name = _PropertyName(field_descriptor.name)
|
|
label = field_descriptor.label
|
|
cpp_type = field_descriptor.cpp_type
|
|
|
|
# Nonrepeated scalar. Just set the field directly.
|
|
if (label != _FieldDescriptor.LABEL_REPEATED
|
|
and cpp_type != _FieldDescriptor.CPPTYPE_MESSAGE):
|
|
scalar_setter_fn(_DeserializeScalarFromDecoder(field_type, decoder))
|
|
return decoder.Position() - initial_position
|
|
|
|
# Nonrepeated composite. Recursively deserialize.
|
|
if label != _FieldDescriptor.LABEL_REPEATED:
|
|
composite = value
|
|
_RecursivelyMerge(field_number, field_type, decoder, composite)
|
|
return decoder.Position() - initial_position
|
|
|
|
# Now we know we're dealing with a repeated field of some kind.
|
|
element_list = value
|
|
|
|
if cpp_type != _FieldDescriptor.CPPTYPE_MESSAGE:
|
|
# Repeated scalar.
|
|
element_list.append(_DeserializeScalarFromDecoder(field_type, decoder))
|
|
return decoder.Position() - initial_position
|
|
else:
|
|
# Repeated composite.
|
|
composite = element_list.add()
|
|
_RecursivelyMerge(field_number, field_type, decoder, composite)
|
|
return decoder.Position() - initial_position
|
|
|
|
|
|
def _FieldOrExtensionValues(message, field_or_extension):
|
|
"""Retrieves the list of values for the specified field or extension.
|
|
|
|
The target field or extension can be optional, required or repeated, but it
|
|
must have value(s) set. The assumption is that the target field or extension
|
|
is set (e.g. _HasFieldOrExtension holds true).
|
|
|
|
Args:
|
|
message: Message which contains the target field or extension.
|
|
field_or_extension: Field or extension for which the list of values is
|
|
required. Must be an instance of FieldDescriptor.
|
|
|
|
Returns:
|
|
A list of values for the specified field or extension. This list will only
|
|
contain a single element if the field is non-repeated.
|
|
"""
|
|
if field_or_extension.is_extension:
|
|
value = message.Extensions[field_or_extension]
|
|
else:
|
|
value = getattr(message, _ValueFieldName(field_or_extension.name))
|
|
if field_or_extension.label != _FieldDescriptor.LABEL_REPEATED:
|
|
return [value]
|
|
else:
|
|
# In this case value is a list or repeated values.
|
|
return value
|
|
|
|
|
|
def _HasFieldOrExtension(message, field_or_extension):
|
|
"""Checks if a message has the specified field or extension set.
|
|
|
|
The field or extension specified can be optional, required or repeated. If
|
|
it is repeated, this function returns True. Otherwise it checks the has bit
|
|
of the field or extension.
|
|
|
|
Args:
|
|
message: Message which contains the target field or extension.
|
|
field_or_extension: Field or extension to check. This must be a
|
|
FieldDescriptor instance.
|
|
|
|
Returns:
|
|
True if the message has a value set for the specified field or extension,
|
|
or if the field or extension is repeated.
|
|
"""
|
|
if field_or_extension.label == _FieldDescriptor.LABEL_REPEATED:
|
|
return True
|
|
if field_or_extension.is_extension:
|
|
return message.HasExtension(field_or_extension)
|
|
else:
|
|
return message.HasField(field_or_extension.name)
|
|
|
|
|
|
def _IsFieldOrExtensionInitialized(message, field, errors=None):
|
|
"""Checks if a message field or extension is initialized.
|
|
|
|
Args:
|
|
message: The message which contains the field or extension.
|
|
field: Field or extension to check. This must be a FieldDescriptor instance.
|
|
errors: Errors will be appended to it, if set to a meaningful value.
|
|
|
|
Returns:
|
|
True if the field/extension can be considered initialized.
|
|
"""
|
|
# If the field is required and is not set, it isn't initialized.
|
|
if field.label == _FieldDescriptor.LABEL_REQUIRED:
|
|
if not _HasFieldOrExtension(message, field):
|
|
if errors is not None:
|
|
errors.append('Required field %s is not set.' % field.full_name)
|
|
return False
|
|
|
|
# If the field is optional and is not set, or if it
|
|
# isn't a submessage then the field is initialized.
|
|
if field.label == _FieldDescriptor.LABEL_OPTIONAL:
|
|
if not _HasFieldOrExtension(message, field):
|
|
return True
|
|
if field.cpp_type != _FieldDescriptor.CPPTYPE_MESSAGE:
|
|
return True
|
|
|
|
# The field is set and is either a single or a repeated submessage.
|
|
messages = _FieldOrExtensionValues(message, field)
|
|
# If all submessages in this field are initialized, the field is
|
|
# considered initialized.
|
|
for message in messages:
|
|
if not _InternalIsInitialized(message, errors):
|
|
return False
|
|
return True
|
|
|
|
|
|
def _InternalIsInitialized(message, errors=None):
|
|
"""Checks if all required fields of a message are set.
|
|
|
|
Args:
|
|
message: The message to check.
|
|
errors: If set, initialization errors will be appended to it.
|
|
|
|
Returns:
|
|
True iff the specified message has all required fields set.
|
|
"""
|
|
fields_and_extensions = []
|
|
fields_and_extensions.extend(message.DESCRIPTOR.fields)
|
|
fields_and_extensions.extend(
|
|
[extension[0] for extension in message.Extensions._ListSetExtensions()])
|
|
for field_or_extension in fields_and_extensions:
|
|
if not _IsFieldOrExtensionInitialized(message, field_or_extension, errors):
|
|
return False
|
|
return True
|
|
|
|
|
|
def _AddMergeFromStringMethod(message_descriptor, cls):
|
|
"""Helper for _AddMessageMethods()."""
|
|
Decoder = decoder.Decoder
|
|
def MergeFromString(self, serialized):
|
|
decoder = Decoder(serialized)
|
|
byte_count = 0
|
|
while not decoder.EndOfStream():
|
|
bytes_read = _DeserializeOneEntity(message_descriptor, self, decoder)
|
|
if not bytes_read:
|
|
break
|
|
byte_count += bytes_read
|
|
return byte_count
|
|
cls.MergeFromString = MergeFromString
|
|
|
|
|
|
def _AddIsInitializedMethod(cls):
|
|
"""Adds the IsInitialized method to the protocol message class."""
|
|
cls.IsInitialized = _InternalIsInitialized
|
|
|
|
|
|
def _MergeFieldOrExtension(destination_msg, field, value):
|
|
"""Merges a specified message field into another message."""
|
|
property_name = _PropertyName(field.name)
|
|
is_extension = field.is_extension
|
|
|
|
if not is_extension:
|
|
destination = getattr(destination_msg, property_name)
|
|
elif (field.label == _FieldDescriptor.LABEL_REPEATED or
|
|
field.cpp_type == _FieldDescriptor.CPPTYPE_MESSAGE):
|
|
destination = destination_msg.Extensions[field]
|
|
|
|
# Case 1 - a composite field.
|
|
if field.cpp_type == _FieldDescriptor.CPPTYPE_MESSAGE:
|
|
if field.label == _FieldDescriptor.LABEL_REPEATED:
|
|
for v in value:
|
|
destination.add().MergeFrom(v)
|
|
else:
|
|
destination.MergeFrom(value)
|
|
return
|
|
|
|
# Case 2 - a repeated field.
|
|
if field.label == _FieldDescriptor.LABEL_REPEATED:
|
|
for v in value:
|
|
destination.append(v)
|
|
return
|
|
|
|
# Case 3 - a singular field.
|
|
if is_extension:
|
|
destination_msg.Extensions[field] = value
|
|
else:
|
|
setattr(destination_msg, property_name, value)
|
|
|
|
|
|
def _AddMergeFromMethod(cls):
|
|
def MergeFrom(self, msg):
|
|
assert msg is not self
|
|
for field in msg.ListFields():
|
|
_MergeFieldOrExtension(self, field[0], field[1])
|
|
cls.MergeFrom = MergeFrom
|
|
|
|
|
|
def _AddMessageMethods(message_descriptor, cls):
|
|
"""Adds implementations of all Message methods to cls."""
|
|
_AddListFieldsMethod(message_descriptor, cls)
|
|
_AddHasFieldMethod(cls)
|
|
_AddClearFieldMethod(cls)
|
|
_AddClearExtensionMethod(cls)
|
|
_AddClearMethod(cls)
|
|
_AddHasExtensionMethod(cls)
|
|
_AddEqualsMethod(message_descriptor, cls)
|
|
_AddSetListenerMethod(cls)
|
|
_AddByteSizeMethod(message_descriptor, cls)
|
|
_AddSerializeToStringMethod(message_descriptor, cls)
|
|
_AddSerializePartialToStringMethod(message_descriptor, cls)
|
|
_AddMergeFromStringMethod(message_descriptor, cls)
|
|
_AddIsInitializedMethod(cls)
|
|
_AddMergeFromMethod(cls)
|
|
|
|
|
|
def _AddPrivateHelperMethods(cls):
|
|
"""Adds implementation of private helper methods to cls."""
|
|
|
|
def MaybeCallTransitionToNonemptyCallback(self):
|
|
"""Calls self._listener.TransitionToNonempty() the first time this
|
|
method is called. On all subsequent calls, this is a no-op.
|
|
"""
|
|
if not self._called_transition_to_nonempty:
|
|
self._listener.TransitionToNonempty()
|
|
self._called_transition_to_nonempty = True
|
|
cls._MaybeCallTransitionToNonemptyCallback = (
|
|
MaybeCallTransitionToNonemptyCallback)
|
|
|
|
def MarkByteSizeDirty(self):
|
|
"""Sets the _cached_byte_size_dirty bit to true,
|
|
and propagates this to our listener iff this was a state change.
|
|
"""
|
|
if not self._cached_byte_size_dirty:
|
|
self._cached_byte_size_dirty = True
|
|
self._listener.ByteSizeDirty()
|
|
cls._MarkByteSizeDirty = MarkByteSizeDirty
|
|
|
|
|
|
class _Listener(object):
|
|
|
|
"""MessageListener implementation that a parent message registers with its
|
|
child message.
|
|
|
|
In order to support semantics like:
|
|
|
|
foo.bar.baz = 23
|
|
assert foo.HasField('bar')
|
|
|
|
...child objects must have back references to their parents.
|
|
This helper class is at the heart of this support.
|
|
"""
|
|
|
|
def __init__(self, parent_message, has_field_name):
|
|
"""Args:
|
|
parent_message: The message whose _MaybeCallTransitionToNonemptyCallback()
|
|
and _MarkByteSizeDirty() methods we should call when we receive
|
|
TransitionToNonempty() and ByteSizeDirty() messages.
|
|
has_field_name: The name of the "has" field that we should set in
|
|
the parent message when we receive a TransitionToNonempty message,
|
|
or None if there's no "has" field to set. (This will be the case
|
|
for child objects in "repeated" fields).
|
|
"""
|
|
# This listener establishes a back reference from a child (contained) object
|
|
# to its parent (containing) object. We make this a weak reference to avoid
|
|
# creating cyclic garbage when the client finishes with the 'parent' object
|
|
# in the tree.
|
|
if isinstance(parent_message, weakref.ProxyType):
|
|
self._parent_message_weakref = parent_message
|
|
else:
|
|
self._parent_message_weakref = weakref.proxy(parent_message)
|
|
self._has_field_name = has_field_name
|
|
|
|
def TransitionToNonempty(self):
|
|
try:
|
|
if self._has_field_name is not None:
|
|
setattr(self._parent_message_weakref, self._has_field_name, True)
|
|
# Propagate the signal to our parents iff this is the first field set.
|
|
self._parent_message_weakref._MaybeCallTransitionToNonemptyCallback()
|
|
except ReferenceError:
|
|
# We can get here if a client has kept a reference to a child object,
|
|
# and is now setting a field on it, but the child's parent has been
|
|
# garbage-collected. This is not an error.
|
|
pass
|
|
|
|
def ByteSizeDirty(self):
|
|
try:
|
|
self._parent_message_weakref._MarkByteSizeDirty()
|
|
except ReferenceError:
|
|
# Same as above.
|
|
pass
|
|
|
|
|
|
# TODO(robinson): Move elsewhere?
|
|
# TODO(robinson): Provide a clear() method here in addition to ClearField()?
|
|
class _RepeatedScalarFieldContainer(object):
|
|
|
|
"""Simple, type-checked, list-like container for holding repeated scalars."""
|
|
|
|
# Minimizes memory usage and disallows assignment to other attributes.
|
|
__slots__ = ['_message_listener', '_type_checker', '_values']
|
|
|
|
def __init__(self, message_listener, type_checker):
|
|
"""
|
|
Args:
|
|
message_listener: A MessageListener implementation.
|
|
The _RepeatedScalarFieldContaininer will call this object's
|
|
TransitionToNonempty() method when it transitions from being empty to
|
|
being nonempty.
|
|
type_checker: A _ValueChecker instance to run on elements inserted
|
|
into this container.
|
|
"""
|
|
self._message_listener = message_listener
|
|
self._type_checker = type_checker
|
|
self._values = []
|
|
|
|
def append(self, elem):
|
|
self._type_checker.CheckValue(elem)
|
|
self._values.append(elem)
|
|
self._message_listener.ByteSizeDirty()
|
|
if len(self._values) == 1:
|
|
self._message_listener.TransitionToNonempty()
|
|
|
|
def remove(self, elem):
|
|
self._values.remove(elem)
|
|
self._message_listener.ByteSizeDirty()
|
|
|
|
# List-like __getitem__() support also makes us iterable (via "iter(foo)"
|
|
# or implicitly via "for i in mylist:") for free.
|
|
def __getitem__(self, key):
|
|
return self._values[key]
|
|
|
|
def __setitem__(self, key, value):
|
|
# No need to call TransitionToNonempty(), since if we're able to
|
|
# set the element at this index, we were already nonempty before
|
|
# this method was called.
|
|
self._message_listener.ByteSizeDirty()
|
|
self._type_checker.CheckValue(value)
|
|
self._values[key] = value
|
|
|
|
def __len__(self):
|
|
return len(self._values)
|
|
|
|
def __eq__(self, other):
|
|
if self is other:
|
|
return True
|
|
# Special case for the same type which should be common and fast.
|
|
if isinstance(other, self.__class__):
|
|
return other._values == self._values
|
|
# We are presumably comparing against some other sequence type.
|
|
return other == self._values
|
|
|
|
def __ne__(self, other):
|
|
# Can't use != here since it would infinitely recurse.
|
|
return not self == other
|
|
|
|
|
|
# TODO(robinson): Move elsewhere?
|
|
# TODO(robinson): Provide a clear() method here in addition to ClearField()?
|
|
# TODO(robinson): Unify common functionality with
|
|
# _RepeatedScalarFieldContaininer?
|
|
class _RepeatedCompositeFieldContainer(object):
|
|
|
|
"""Simple, list-like container for holding repeated composite fields."""
|
|
|
|
# Minimizes memory usage and disallows assignment to other attributes.
|
|
__slots__ = ['_values', '_message_descriptor', '_message_listener']
|
|
|
|
def __init__(self, message_listener, message_descriptor):
|
|
"""Note that we pass in a descriptor instead of the generated directly,
|
|
since at the time we construct a _RepeatedCompositeFieldContainer we
|
|
haven't yet necessarily initialized the type that will be contained in the
|
|
container.
|
|
|
|
Args:
|
|
message_listener: A MessageListener implementation.
|
|
The _RepeatedCompositeFieldContainer will call this object's
|
|
TransitionToNonempty() method when it transitions from being empty to
|
|
being nonempty.
|
|
message_descriptor: A Descriptor instance describing the protocol type
|
|
that should be present in this container. We'll use the
|
|
_concrete_class field of this descriptor when the client calls add().
|
|
"""
|
|
self._message_listener = message_listener
|
|
self._message_descriptor = message_descriptor
|
|
self._values = []
|
|
|
|
def add(self):
|
|
new_element = self._message_descriptor._concrete_class()
|
|
new_element._SetListener(self._message_listener)
|
|
self._values.append(new_element)
|
|
self._message_listener.ByteSizeDirty()
|
|
self._message_listener.TransitionToNonempty()
|
|
return new_element
|
|
|
|
def __delitem__(self, key):
|
|
self._message_listener.ByteSizeDirty()
|
|
del self._values[key]
|
|
|
|
# List-like __getitem__() support also makes us iterable (via "iter(foo)"
|
|
# or implicitly via "for i in mylist:") for free.
|
|
def __getitem__(self, key):
|
|
return self._values[key]
|
|
|
|
def __len__(self):
|
|
return len(self._values)
|
|
|
|
def __eq__(self, other):
|
|
if self is other:
|
|
return True
|
|
if not isinstance(other, self.__class__):
|
|
raise TypeError('Can only compare repeated composite fields against '
|
|
'other repeated composite fields.')
|
|
return self._values == other._values
|
|
|
|
def __ne__(self, other):
|
|
# Can't use != here since it would infinitely recurse.
|
|
return not self == other
|
|
|
|
# TODO(robinson): Implement, document, and test slicing support.
|
|
|
|
|
|
# TODO(robinson): Move elsewhere? This file is getting pretty ridiculous...
|
|
# TODO(robinson): Unify error handling of "unknown extension" crap.
|
|
# TODO(robinson): There's so much similarity between the way that
|
|
# extensions behave and the way that normal fields behave that it would
|
|
# be really nice to unify more code. It's not immediately obvious
|
|
# how to do this, though, and I'd rather get the full functionality
|
|
# implemented (and, crucially, get all the tests and specs fleshed out
|
|
# and passing), and then come back to this thorny unification problem.
|
|
# TODO(robinson): Support iteritems()-style iteration over all
|
|
# extensions with the "has" bits turned on?
|
|
class _ExtensionDict(object):
|
|
|
|
"""Dict-like container for supporting an indexable "Extensions"
|
|
field on proto instances.
|
|
|
|
Note that in all cases we expect extension handles to be
|
|
FieldDescriptors.
|
|
"""
|
|
|
|
class _ExtensionListener(object):
|
|
|
|
"""Adapts an _ExtensionDict to behave as a MessageListener."""
|
|
|
|
def __init__(self, extension_dict, handle_id):
|
|
self._extension_dict = extension_dict
|
|
self._handle_id = handle_id
|
|
|
|
def TransitionToNonempty(self):
|
|
self._extension_dict._SubmessageTransitionedToNonempty(self._handle_id)
|
|
|
|
def ByteSizeDirty(self):
|
|
self._extension_dict._SubmessageByteSizeBecameDirty()
|
|
|
|
# TODO(robinson): Somewhere, we need to blow up if people
|
|
# try to register two extensions with the same field number.
|
|
# (And we need a test for this of course).
|
|
|
|
def __init__(self, extended_message, known_extensions):
|
|
"""extended_message: Message instance for which we are the Extensions dict.
|
|
known_extensions: Iterable of known extension handles.
|
|
These must be FieldDescriptors.
|
|
"""
|
|
# We keep a weak reference to extended_message, since
|
|
# it has a reference to this instance in turn.
|
|
self._extended_message = weakref.proxy(extended_message)
|
|
# We make a deep copy of known_extensions to avoid any
|
|
# thread-safety concerns, since the argument passed in
|
|
# is the global (class-level) dict of known extensions for
|
|
# this type of message, which could be modified at any time
|
|
# via a RegisterExtension() call.
|
|
#
|
|
# This dict maps from handle id to handle (a FieldDescriptor).
|
|
#
|
|
# XXX
|
|
# TODO(robinson): This isn't good enough. The client could
|
|
# instantiate an object in module A, then afterward import
|
|
# module B and pass the instance to B.Foo(). If B imports
|
|
# an extender of this proto and then tries to use it, B
|
|
# will get a KeyError, even though the extension *is* registered
|
|
# at the time of use.
|
|
# XXX
|
|
self._known_extensions = dict((id(e), e) for e in known_extensions)
|
|
# Read lock around self._values, which may be modified by multiple
|
|
# concurrent readers in the conceptually "const" __getitem__ method.
|
|
# So, we grab this lock in every "read-only" method to ensure
|
|
# that concurrent read access is safe without external locking.
|
|
self._lock = threading.Lock()
|
|
# Maps from extension handle ID to current value of that extension.
|
|
self._values = {}
|
|
# Maps from extension handle ID to a boolean "has" bit, but only
|
|
# for non-repeated extension fields.
|
|
keys = (id for id, extension in self._known_extensions.iteritems()
|
|
if extension.label != _FieldDescriptor.LABEL_REPEATED)
|
|
self._has_bits = dict.fromkeys(keys, False)
|
|
|
|
def __getitem__(self, extension_handle):
|
|
"""Returns the current value of the given extension handle."""
|
|
# We don't care as much about keeping critical sections short in the
|
|
# extension support, since it's presumably much less of a common case.
|
|
self._lock.acquire()
|
|
try:
|
|
handle_id = id(extension_handle)
|
|
if handle_id not in self._known_extensions:
|
|
raise KeyError('Extension not known to this class')
|
|
if handle_id not in self._values:
|
|
self._AddMissingHandle(extension_handle, handle_id)
|
|
return self._values[handle_id]
|
|
finally:
|
|
self._lock.release()
|
|
|
|
def __eq__(self, other):
|
|
# We have to grab read locks since we're accessing _values
|
|
# in a "const" method. See the comment in the constructor.
|
|
if self is other:
|
|
return True
|
|
self._lock.acquire()
|
|
try:
|
|
other._lock.acquire()
|
|
try:
|
|
if self._has_bits != other._has_bits:
|
|
return False
|
|
# If there's a "has" bit, then only compare values where it is true.
|
|
for k, v in self._values.iteritems():
|
|
if self._has_bits.get(k, False) and v != other._values[k]:
|
|
return False
|
|
return True
|
|
finally:
|
|
other._lock.release()
|
|
finally:
|
|
self._lock.release()
|
|
|
|
def __ne__(self, other):
|
|
return not self == other
|
|
|
|
# Note that this is only meaningful for non-repeated, scalar extension
|
|
# fields. Note also that we may have to call
|
|
# MaybeCallTransitionToNonemptyCallback() when we do successfully set a field
|
|
# this way, to set any necssary "has" bits in the ancestors of the extended
|
|
# message.
|
|
def __setitem__(self, extension_handle, value):
|
|
"""If extension_handle specifies a non-repeated, scalar extension
|
|
field, sets the value of that field.
|
|
"""
|
|
handle_id = id(extension_handle)
|
|
if handle_id not in self._known_extensions:
|
|
raise KeyError('Extension not known to this class')
|
|
field = extension_handle # Just shorten the name.
|
|
if (field.label == _FieldDescriptor.LABEL_OPTIONAL
|
|
and field.cpp_type != _FieldDescriptor.CPPTYPE_MESSAGE):
|
|
# It's slightly wasteful to lookup the type checker each time,
|
|
# but we expect this to be a vanishingly uncommon case anyway.
|
|
type_checker = type_checkers.GetTypeChecker(field.cpp_type, field.type)
|
|
type_checker.CheckValue(value)
|
|
self._values[handle_id] = value
|
|
self._has_bits[handle_id] = True
|
|
self._extended_message._MarkByteSizeDirty()
|
|
self._extended_message._MaybeCallTransitionToNonemptyCallback()
|
|
else:
|
|
raise TypeError('Extension is repeated and/or a composite type.')
|
|
|
|
def _AddMissingHandle(self, extension_handle, handle_id):
|
|
"""Helper internal to ExtensionDict."""
|
|
# Special handling for non-repeated message extensions, which (like
|
|
# normal fields of this kind) are initialized lazily.
|
|
# REQUIRES: _lock already held.
|
|
cpp_type = extension_handle.cpp_type
|
|
label = extension_handle.label
|
|
if (cpp_type == _FieldDescriptor.CPPTYPE_MESSAGE
|
|
and label != _FieldDescriptor.LABEL_REPEATED):
|
|
self._AddMissingNonRepeatedCompositeHandle(extension_handle, handle_id)
|
|
else:
|
|
self._values[handle_id] = _DefaultValueForField(
|
|
self._extended_message, extension_handle)
|
|
|
|
def _AddMissingNonRepeatedCompositeHandle(self, extension_handle, handle_id):
|
|
"""Helper internal to ExtensionDict."""
|
|
# REQUIRES: _lock already held.
|
|
value = extension_handle.message_type._concrete_class()
|
|
value._SetListener(_ExtensionDict._ExtensionListener(self, handle_id))
|
|
self._values[handle_id] = value
|
|
|
|
def _SubmessageTransitionedToNonempty(self, handle_id):
|
|
"""Called when a submessage with a given handle id first transitions to
|
|
being nonempty. Called by _ExtensionListener.
|
|
"""
|
|
assert handle_id in self._has_bits
|
|
self._has_bits[handle_id] = True
|
|
self._extended_message._MaybeCallTransitionToNonemptyCallback()
|
|
|
|
def _SubmessageByteSizeBecameDirty(self):
|
|
"""Called whenever a submessage's cached byte size becomes invalid
|
|
(goes from being "clean" to being "dirty"). Called by _ExtensionListener.
|
|
"""
|
|
self._extended_message._MarkByteSizeDirty()
|
|
|
|
# We may wish to widen the public interface of Message.Extensions
|
|
# to expose some of this private functionality in the future.
|
|
# For now, we make all this functionality module-private and just
|
|
# implement what we need for serialization/deserialization,
|
|
# HasField()/ClearField(), etc.
|
|
|
|
def _HasExtension(self, extension_handle):
|
|
"""Method for internal use by this module.
|
|
Returns true iff we "have" this extension in the sense of the
|
|
"has" bit being set.
|
|
"""
|
|
handle_id = id(extension_handle)
|
|
# Note that this is different from the other checks.
|
|
if handle_id not in self._has_bits:
|
|
raise KeyError('Extension not known to this class, or is repeated field.')
|
|
return self._has_bits[handle_id]
|
|
|
|
# Intentionally pretty similar to ClearField() above.
|
|
def _ClearExtension(self, extension_handle):
|
|
"""Method for internal use by this module.
|
|
Clears the specified extension, unsetting its "has" bit.
|
|
"""
|
|
handle_id = id(extension_handle)
|
|
if handle_id not in self._known_extensions:
|
|
raise KeyError('Extension not known to this class')
|
|
default_value = _DefaultValueForField(self._extended_message,
|
|
extension_handle)
|
|
if extension_handle.label == _FieldDescriptor.LABEL_REPEATED:
|
|
self._extended_message._MarkByteSizeDirty()
|
|
else:
|
|
cpp_type = extension_handle.cpp_type
|
|
if cpp_type == _FieldDescriptor.CPPTYPE_MESSAGE:
|
|
if handle_id in self._values:
|
|
# Future modifications to this object shouldn't set any
|
|
# "has" bits here.
|
|
self._values[handle_id]._SetListener(None)
|
|
if self._has_bits[handle_id]:
|
|
self._has_bits[handle_id] = False
|
|
self._extended_message._MarkByteSizeDirty()
|
|
if handle_id in self._values:
|
|
del self._values[handle_id]
|
|
|
|
def _ListSetExtensions(self):
|
|
"""Method for internal use by this module.
|
|
|
|
Returns an sequence of all extensions that are currently "set"
|
|
in this extension dict. A "set" extension is a repeated extension,
|
|
or a non-repeated extension with its "has" bit set.
|
|
|
|
The returned sequence contains (field_descriptor, value) pairs,
|
|
where value is the current value of the extension with the given
|
|
field descriptor.
|
|
|
|
The sequence values are in arbitrary order.
|
|
"""
|
|
self._lock.acquire() # Read-only methods must lock around self._values.
|
|
try:
|
|
set_extensions = []
|
|
for handle_id, value in self._values.iteritems():
|
|
handle = self._known_extensions[handle_id]
|
|
if (handle.label == _FieldDescriptor.LABEL_REPEATED
|
|
or self._has_bits[handle_id]):
|
|
set_extensions.append((handle, value))
|
|
return set_extensions
|
|
finally:
|
|
self._lock.release()
|
|
|
|
def _AllExtensionsByNumber(self):
|
|
"""Method for internal use by this module.
|
|
|
|
Returns: A dict mapping field_number to (handle, field_descriptor),
|
|
for *all* registered extensions for this dict.
|
|
"""
|
|
# TODO(robinson): Precompute and store this away. Note that we'll have to
|
|
# be careful when we move away from having _known_extensions as a
|
|
# deep-copied member of this object.
|
|
return dict((f.number, f) for f in self._known_extensions.itervalues())
|