| Index: third_party/protobuf/python/google/protobuf/internal/containers.py
|
| diff --git a/third_party/protobuf/python/google/protobuf/internal/containers.py b/third_party/protobuf/python/google/protobuf/internal/containers.py
|
| index 34b35f8ab2da7bd17743c9d0e0f233e08e68ff5c..97cdd848e34d8da4083abb512b7403686ddec91f 100755
|
| --- a/third_party/protobuf/python/google/protobuf/internal/containers.py
|
| +++ b/third_party/protobuf/python/google/protobuf/internal/containers.py
|
| @@ -1,6 +1,6 @@
|
| # Protocol Buffers - Google's data interchange format
|
| # Copyright 2008 Google Inc. All rights reserved.
|
| -# http://code.google.com/p/protobuf/
|
| +# https://developers.google.com/protocol-buffers/
|
| #
|
| # Redistribution and use in source and binary forms, with or without
|
| # modification, are permitted provided that the following conditions are
|
| @@ -41,6 +41,146 @@ are:
|
|
|
| __author__ = 'petar@google.com (Petar Petrov)'
|
|
|
| +import collections
|
| +import sys
|
| +
|
| +if sys.version_info[0] < 3:
|
| + # We would use collections.MutableMapping all the time, but in Python 2 it
|
| + # doesn't define __slots__. This causes two significant problems:
|
| + #
|
| + # 1. we can't disallow arbitrary attribute assignment, even if our derived
|
| + # classes *do* define __slots__.
|
| + #
|
| + # 2. we can't safely derive a C type from it without __slots__ defined (the
|
| + # interpreter expects to find a dict at tp_dictoffset, which we can't
|
| + # robustly provide. And we don't want an instance dict anyway.
|
| + #
|
| + # So this is the Python 2.7 definition of Mapping/MutableMapping functions
|
| + # verbatim, except that:
|
| + # 1. We declare __slots__.
|
| + # 2. We don't declare this as a virtual base class. The classes defined
|
| + # in collections are the interesting base classes, not us.
|
| + #
|
| + # Note: deriving from object is critical. It is the only thing that makes
|
| + # this a true type, allowing us to derive from it in C++ cleanly and making
|
| + # __slots__ properly disallow arbitrary element assignment.
|
| +
|
| + class Mapping(object):
|
| + __slots__ = ()
|
| +
|
| + def get(self, key, default=None):
|
| + try:
|
| + return self[key]
|
| + except KeyError:
|
| + return default
|
| +
|
| + def __contains__(self, key):
|
| + try:
|
| + self[key]
|
| + except KeyError:
|
| + return False
|
| + else:
|
| + return True
|
| +
|
| + def iterkeys(self):
|
| + return iter(self)
|
| +
|
| + def itervalues(self):
|
| + for key in self:
|
| + yield self[key]
|
| +
|
| + def iteritems(self):
|
| + for key in self:
|
| + yield (key, self[key])
|
| +
|
| + def keys(self):
|
| + return list(self)
|
| +
|
| + def items(self):
|
| + return [(key, self[key]) for key in self]
|
| +
|
| + def values(self):
|
| + return [self[key] for key in self]
|
| +
|
| + # Mappings are not hashable by default, but subclasses can change this
|
| + __hash__ = None
|
| +
|
| + def __eq__(self, other):
|
| + if not isinstance(other, collections.Mapping):
|
| + return NotImplemented
|
| + return dict(self.items()) == dict(other.items())
|
| +
|
| + def __ne__(self, other):
|
| + return not (self == other)
|
| +
|
| + class MutableMapping(Mapping):
|
| + __slots__ = ()
|
| +
|
| + __marker = object()
|
| +
|
| + def pop(self, key, default=__marker):
|
| + try:
|
| + value = self[key]
|
| + except KeyError:
|
| + if default is self.__marker:
|
| + raise
|
| + return default
|
| + else:
|
| + del self[key]
|
| + return value
|
| +
|
| + def popitem(self):
|
| + try:
|
| + key = next(iter(self))
|
| + except StopIteration:
|
| + raise KeyError
|
| + value = self[key]
|
| + del self[key]
|
| + return key, value
|
| +
|
| + def clear(self):
|
| + try:
|
| + while True:
|
| + self.popitem()
|
| + except KeyError:
|
| + pass
|
| +
|
| + def update(*args, **kwds):
|
| + if len(args) > 2:
|
| + raise TypeError("update() takes at most 2 positional "
|
| + "arguments ({} given)".format(len(args)))
|
| + elif not args:
|
| + raise TypeError("update() takes at least 1 argument (0 given)")
|
| + self = args[0]
|
| + other = args[1] if len(args) >= 2 else ()
|
| +
|
| + if isinstance(other, Mapping):
|
| + for key in other:
|
| + self[key] = other[key]
|
| + elif hasattr(other, "keys"):
|
| + for key in other.keys():
|
| + self[key] = other[key]
|
| + else:
|
| + for key, value in other:
|
| + self[key] = value
|
| + for key, value in kwds.items():
|
| + self[key] = value
|
| +
|
| + def setdefault(self, key, default=None):
|
| + try:
|
| + return self[key]
|
| + except KeyError:
|
| + self[key] = default
|
| + return default
|
| +
|
| + collections.Mapping.register(Mapping)
|
| + collections.MutableMapping.register(MutableMapping)
|
| +
|
| +else:
|
| + # In Python 3 we can just use MutableMapping directly, because it defines
|
| + # __slots__.
|
| + MutableMapping = collections.MutableMapping
|
| +
|
|
|
| class BaseContainer(object):
|
|
|
| @@ -108,29 +248,34 @@ class RepeatedScalarFieldContainer(BaseContainer):
|
|
|
| def append(self, value):
|
| """Appends an item to the list. Similar to list.append()."""
|
| - self._type_checker.CheckValue(value)
|
| - self._values.append(value)
|
| + self._values.append(self._type_checker.CheckValue(value))
|
| if not self._message_listener.dirty:
|
| self._message_listener.Modified()
|
|
|
| def insert(self, key, value):
|
| """Inserts the item at the specified position. Similar to list.insert()."""
|
| - self._type_checker.CheckValue(value)
|
| - self._values.insert(key, value)
|
| + self._values.insert(key, self._type_checker.CheckValue(value))
|
| if not self._message_listener.dirty:
|
| self._message_listener.Modified()
|
|
|
| def extend(self, elem_seq):
|
| - """Extends by appending the given sequence. Similar to list.extend()."""
|
| - if not elem_seq:
|
| - return
|
| + """Extends by appending the given iterable. Similar to list.extend()."""
|
|
|
| - new_values = []
|
| - for elem in elem_seq:
|
| - self._type_checker.CheckValue(elem)
|
| - new_values.append(elem)
|
| - self._values.extend(new_values)
|
| - self._message_listener.Modified()
|
| + if elem_seq is None:
|
| + return
|
| + try:
|
| + elem_seq_iter = iter(elem_seq)
|
| + except TypeError:
|
| + if not elem_seq:
|
| + # silently ignore falsy inputs :-/.
|
| + # TODO(ptucker): Deprecate this behavior. b/18413862
|
| + return
|
| + raise
|
| +
|
| + new_values = [self._type_checker.CheckValue(elem) for elem in elem_seq_iter]
|
| + if new_values:
|
| + self._values.extend(new_values)
|
| + self._message_listener.Modified()
|
|
|
| def MergeFrom(self, other):
|
| """Appends the contents of another repeated field of the same type to this
|
| @@ -144,11 +289,21 @@ class RepeatedScalarFieldContainer(BaseContainer):
|
| self._values.remove(elem)
|
| self._message_listener.Modified()
|
|
|
| + def pop(self, key=-1):
|
| + """Removes and returns an item at a given index. Similar to list.pop()."""
|
| + value = self._values[key]
|
| + self.__delitem__(key)
|
| + return value
|
| +
|
| def __setitem__(self, key, value):
|
| """Sets the item on the specified position."""
|
| - self._type_checker.CheckValue(value)
|
| - self._values[key] = value
|
| - self._message_listener.Modified()
|
| + if isinstance(key, slice): # PY3
|
| + if key.step is not None:
|
| + raise ValueError('Extended slices not supported')
|
| + self.__setslice__(key.start, key.stop, value)
|
| + else:
|
| + self._values[key] = self._type_checker.CheckValue(value)
|
| + self._message_listener.Modified()
|
|
|
| def __getslice__(self, start, stop):
|
| """Retrieves the subset of items from between the specified indices."""
|
| @@ -158,8 +313,7 @@ class RepeatedScalarFieldContainer(BaseContainer):
|
| """Sets the subset of items from between the specified indices."""
|
| new_values = []
|
| for value in values:
|
| - self._type_checker.CheckValue(value)
|
| - new_values.append(value)
|
| + new_values.append(self._type_checker.CheckValue(value))
|
| self._values[start:stop] = new_values
|
| self._message_listener.Modified()
|
|
|
| @@ -183,6 +337,8 @@ class RepeatedScalarFieldContainer(BaseContainer):
|
| # We are presumably comparing against some other sequence type.
|
| return other == self._values
|
|
|
| +collections.MutableSequence.register(BaseContainer)
|
| +
|
|
|
| class RepeatedCompositeFieldContainer(BaseContainer):
|
|
|
| @@ -245,6 +401,12 @@ class RepeatedCompositeFieldContainer(BaseContainer):
|
| self._values.remove(elem)
|
| self._message_listener.Modified()
|
|
|
| + def pop(self, key=-1):
|
| + """Removes and returns an item at a given index. Similar to list.pop()."""
|
| + value = self._values[key]
|
| + self.__delitem__(key)
|
| + return value
|
| +
|
| def __getslice__(self, start, stop):
|
| """Retrieves the subset of items from between the specified indices."""
|
| return self._values[start:stop]
|
| @@ -267,3 +429,183 @@ class RepeatedCompositeFieldContainer(BaseContainer):
|
| raise TypeError('Can only compare repeated composite fields against '
|
| 'other repeated composite fields.')
|
| return self._values == other._values
|
| +
|
| +
|
| +class ScalarMap(MutableMapping):
|
| +
|
| + """Simple, type-checked, dict-like container for holding repeated scalars."""
|
| +
|
| + # Disallows assignment to other attributes.
|
| + __slots__ = ['_key_checker', '_value_checker', '_values', '_message_listener']
|
| +
|
| + def __init__(self, message_listener, key_checker, value_checker):
|
| + """
|
| + Args:
|
| + message_listener: A MessageListener implementation.
|
| + The ScalarMap will call this object's Modified() method when it
|
| + is modified.
|
| + key_checker: A type_checkers.ValueChecker instance to run on keys
|
| + inserted into this container.
|
| + value_checker: A type_checkers.ValueChecker instance to run on values
|
| + inserted into this container.
|
| + """
|
| + self._message_listener = message_listener
|
| + self._key_checker = key_checker
|
| + self._value_checker = value_checker
|
| + self._values = {}
|
| +
|
| + def __getitem__(self, key):
|
| + try:
|
| + return self._values[key]
|
| + except KeyError:
|
| + key = self._key_checker.CheckValue(key)
|
| + val = self._value_checker.DefaultValue()
|
| + self._values[key] = val
|
| + return val
|
| +
|
| + def __contains__(self, item):
|
| + # We check the key's type to match the strong-typing flavor of the API.
|
| + # Also this makes it easier to match the behavior of the C++ implementation.
|
| + self._key_checker.CheckValue(item)
|
| + return item in self._values
|
| +
|
| + # We need to override this explicitly, because our defaultdict-like behavior
|
| + # will make the default implementation (from our base class) always insert
|
| + # the key.
|
| + def get(self, key, default=None):
|
| + if key in self:
|
| + return self[key]
|
| + else:
|
| + return default
|
| +
|
| + def __setitem__(self, key, value):
|
| + checked_key = self._key_checker.CheckValue(key)
|
| + checked_value = self._value_checker.CheckValue(value)
|
| + self._values[checked_key] = checked_value
|
| + self._message_listener.Modified()
|
| +
|
| + def __delitem__(self, key):
|
| + del self._values[key]
|
| + self._message_listener.Modified()
|
| +
|
| + def __len__(self):
|
| + return len(self._values)
|
| +
|
| + def __iter__(self):
|
| + return iter(self._values)
|
| +
|
| + def __repr__(self):
|
| + return repr(self._values)
|
| +
|
| + def MergeFrom(self, other):
|
| + self._values.update(other._values)
|
| + self._message_listener.Modified()
|
| +
|
| + def InvalidateIterators(self):
|
| + # It appears that the only way to reliably invalidate iterators to
|
| + # self._values is to ensure that its size changes.
|
| + original = self._values
|
| + self._values = original.copy()
|
| + original[None] = None
|
| +
|
| + # This is defined in the abstract base, but we can do it much more cheaply.
|
| + def clear(self):
|
| + self._values.clear()
|
| + self._message_listener.Modified()
|
| +
|
| +
|
| +class MessageMap(MutableMapping):
|
| +
|
| + """Simple, type-checked, dict-like container for with submessage values."""
|
| +
|
| + # Disallows assignment to other attributes.
|
| + __slots__ = ['_key_checker', '_values', '_message_listener',
|
| + '_message_descriptor']
|
| +
|
| + def __init__(self, message_listener, message_descriptor, key_checker):
|
| + """
|
| + Args:
|
| + message_listener: A MessageListener implementation.
|
| + The ScalarMap will call this object's Modified() method when it
|
| + is modified.
|
| + key_checker: A type_checkers.ValueChecker instance to run on keys
|
| + inserted into this container.
|
| + value_checker: A type_checkers.ValueChecker instance to run on values
|
| + inserted into this container.
|
| + """
|
| + self._message_listener = message_listener
|
| + self._message_descriptor = message_descriptor
|
| + self._key_checker = key_checker
|
| + self._values = {}
|
| +
|
| + def __getitem__(self, key):
|
| + try:
|
| + return self._values[key]
|
| + except KeyError:
|
| + key = self._key_checker.CheckValue(key)
|
| + new_element = self._message_descriptor._concrete_class()
|
| + new_element._SetListener(self._message_listener)
|
| + self._values[key] = new_element
|
| + self._message_listener.Modified()
|
| +
|
| + return new_element
|
| +
|
| + def get_or_create(self, key):
|
| + """get_or_create() is an alias for getitem (ie. map[key]).
|
| +
|
| + Args:
|
| + key: The key to get or create in the map.
|
| +
|
| + This is useful in cases where you want to be explicit that the call is
|
| + mutating the map. This can avoid lint errors for statements like this
|
| + that otherwise would appear to be pointless statements:
|
| +
|
| + msg.my_map[key]
|
| + """
|
| + return self[key]
|
| +
|
| + # We need to override this explicitly, because our defaultdict-like behavior
|
| + # will make the default implementation (from our base class) always insert
|
| + # the key.
|
| + def get(self, key, default=None):
|
| + if key in self:
|
| + return self[key]
|
| + else:
|
| + return default
|
| +
|
| + def __contains__(self, item):
|
| + return item in self._values
|
| +
|
| + def __setitem__(self, key, value):
|
| + raise ValueError('May not set values directly, call my_map[key].foo = 5')
|
| +
|
| + def __delitem__(self, key):
|
| + del self._values[key]
|
| + self._message_listener.Modified()
|
| +
|
| + def __len__(self):
|
| + return len(self._values)
|
| +
|
| + def __iter__(self):
|
| + return iter(self._values)
|
| +
|
| + def __repr__(self):
|
| + return repr(self._values)
|
| +
|
| + def MergeFrom(self, other):
|
| + for key in other:
|
| + self[key].MergeFrom(other[key])
|
| + # self._message_listener.Modified() not required here, because
|
| + # mutations to submessages already propagate.
|
| +
|
| + def InvalidateIterators(self):
|
| + # It appears that the only way to reliably invalidate iterators to
|
| + # self._values is to ensure that its size changes.
|
| + original = self._values
|
| + self._values = original.copy()
|
| + original[None] = None
|
| +
|
| + # This is defined in the abstract base, but we can do it much more cheaply.
|
| + def clear(self):
|
| + self._values.clear()
|
| + self._message_listener.Modified()
|
|
|