• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# Protocol Buffers - Google's data interchange format
2# Copyright 2008 Google Inc.  All rights reserved.
3# https://developers.google.com/protocol-buffers/
4#
5# Redistribution and use in source and binary forms, with or without
6# modification, are permitted provided that the following conditions are
7# met:
8#
9#     * Redistributions of source code must retain the above copyright
10# notice, this list of conditions and the following disclaimer.
11#     * Redistributions in binary form must reproduce the above
12# copyright notice, this list of conditions and the following disclaimer
13# in the documentation and/or other materials provided with the
14# distribution.
15#     * Neither the name of Google Inc. nor the names of its
16# contributors may be used to endorse or promote products derived from
17# this software without specific prior written permission.
18#
19# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
23# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30
31"""Contains container classes to represent different protocol buffer types.
32
33This file defines container classes which represent categories of protocol
34buffer field types which need extra maintenance. Currently these categories
35are:
36  - Repeated scalar fields - These are all repeated fields which aren't
37    composite (e.g. they are of simple types like int32, string, etc).
38  - Repeated composite fields - Repeated fields which are composite. This
39    includes groups and nested messages.
40"""
41
42__author__ = 'petar@google.com (Petar Petrov)'
43
44import collections
45import sys
46
47if sys.version_info[0] < 3:
48  # We would use collections.MutableMapping all the time, but in Python 2 it
49  # doesn't define __slots__.  This causes two significant problems:
50  #
51  # 1. we can't disallow arbitrary attribute assignment, even if our derived
52  #    classes *do* define __slots__.
53  #
54  # 2. we can't safely derive a C type from it without __slots__ defined (the
55  #    interpreter expects to find a dict at tp_dictoffset, which we can't
56  #    robustly provide.  And we don't want an instance dict anyway.
57  #
58  # So this is the Python 2.7 definition of Mapping/MutableMapping functions
59  # verbatim, except that:
60  # 1. We declare __slots__.
61  # 2. We don't declare this as a virtual base class.  The classes defined
62  #    in collections are the interesting base classes, not us.
63  #
64  # Note: deriving from object is critical.  It is the only thing that makes
65  # this a true type, allowing us to derive from it in C++ cleanly and making
66  # __slots__ properly disallow arbitrary element assignment.
67
68  class Mapping(object):
69    __slots__ = ()
70
71    def get(self, key, default=None):
72      try:
73        return self[key]
74      except KeyError:
75        return default
76
77    def __contains__(self, key):
78      try:
79        self[key]
80      except KeyError:
81        return False
82      else:
83        return True
84
85    def iterkeys(self):
86      return iter(self)
87
88    def itervalues(self):
89      for key in self:
90        yield self[key]
91
92    def iteritems(self):
93      for key in self:
94        yield (key, self[key])
95
96    def keys(self):
97      return list(self)
98
99    def items(self):
100      return [(key, self[key]) for key in self]
101
102    def values(self):
103      return [self[key] for key in self]
104
105    # Mappings are not hashable by default, but subclasses can change this
106    __hash__ = None
107
108    def __eq__(self, other):
109      if not isinstance(other, collections.Mapping):
110        return NotImplemented
111      return dict(self.items()) == dict(other.items())
112
113    def __ne__(self, other):
114      return not (self == other)
115
116  class MutableMapping(Mapping):
117    __slots__ = ()
118
119    __marker = object()
120
121    def pop(self, key, default=__marker):
122      try:
123        value = self[key]
124      except KeyError:
125        if default is self.__marker:
126          raise
127        return default
128      else:
129        del self[key]
130        return value
131
132    def popitem(self):
133      try:
134        key = next(iter(self))
135      except StopIteration:
136        raise KeyError
137      value = self[key]
138      del self[key]
139      return key, value
140
141    def clear(self):
142      try:
143        while True:
144          self.popitem()
145      except KeyError:
146        pass
147
148    def update(*args, **kwds):
149      if len(args) > 2:
150        raise TypeError("update() takes at most 2 positional "
151                        "arguments ({} given)".format(len(args)))
152      elif not args:
153        raise TypeError("update() takes at least 1 argument (0 given)")
154      self = args[0]
155      other = args[1] if len(args) >= 2 else ()
156
157      if isinstance(other, Mapping):
158        for key in other:
159          self[key] = other[key]
160      elif hasattr(other, "keys"):
161        for key in other.keys():
162          self[key] = other[key]
163      else:
164        for key, value in other:
165          self[key] = value
166      for key, value in kwds.items():
167        self[key] = value
168
169    def setdefault(self, key, default=None):
170      try:
171        return self[key]
172      except KeyError:
173        self[key] = default
174      return default
175
176  collections.Mapping.register(Mapping)
177  collections.MutableMapping.register(MutableMapping)
178
179else:
180  # In Python 3 we can just use MutableMapping directly, because it defines
181  # __slots__.
182  MutableMapping = collections.MutableMapping
183
184
185class BaseContainer(object):
186
187  """Base container class."""
188
189  # Minimizes memory usage and disallows assignment to other attributes.
190  __slots__ = ['_message_listener', '_values']
191
192  def __init__(self, message_listener):
193    """
194    Args:
195      message_listener: A MessageListener implementation.
196        The RepeatedScalarFieldContainer will call this object's
197        Modified() method when it is modified.
198    """
199    self._message_listener = message_listener
200    self._values = []
201
202  def __getitem__(self, key):
203    """Retrieves item by the specified key."""
204    return self._values[key]
205
206  def __len__(self):
207    """Returns the number of elements in the container."""
208    return len(self._values)
209
210  def __ne__(self, other):
211    """Checks if another instance isn't equal to this one."""
212    # The concrete classes should define __eq__.
213    return not self == other
214
215  def __hash__(self):
216    raise TypeError('unhashable object')
217
218  def __repr__(self):
219    return repr(self._values)
220
221  def sort(self, *args, **kwargs):
222    # Continue to support the old sort_function keyword argument.
223    # This is expected to be a rare occurrence, so use LBYL to avoid
224    # the overhead of actually catching KeyError.
225    if 'sort_function' in kwargs:
226      kwargs['cmp'] = kwargs.pop('sort_function')
227    self._values.sort(*args, **kwargs)
228
229
230class RepeatedScalarFieldContainer(BaseContainer):
231
232  """Simple, type-checked, list-like container for holding repeated scalars."""
233
234  # Disallows assignment to other attributes.
235  __slots__ = ['_type_checker']
236
237  def __init__(self, message_listener, type_checker):
238    """
239    Args:
240      message_listener: A MessageListener implementation.
241        The RepeatedScalarFieldContainer will call this object's
242        Modified() method when it is modified.
243      type_checker: A type_checkers.ValueChecker instance to run on elements
244        inserted into this container.
245    """
246    super(RepeatedScalarFieldContainer, self).__init__(message_listener)
247    self._type_checker = type_checker
248
249  def append(self, value):
250    """Appends an item to the list. Similar to list.append()."""
251    self._values.append(self._type_checker.CheckValue(value))
252    if not self._message_listener.dirty:
253      self._message_listener.Modified()
254
255  def insert(self, key, value):
256    """Inserts the item at the specified position. Similar to list.insert()."""
257    self._values.insert(key, self._type_checker.CheckValue(value))
258    if not self._message_listener.dirty:
259      self._message_listener.Modified()
260
261  def extend(self, elem_seq):
262    """Extends by appending the given iterable. Similar to list.extend()."""
263
264    if elem_seq is None:
265      return
266    try:
267      elem_seq_iter = iter(elem_seq)
268    except TypeError:
269      if not elem_seq:
270        # silently ignore falsy inputs :-/.
271        # TODO(ptucker): Deprecate this behavior. b/18413862
272        return
273      raise
274
275    new_values = [self._type_checker.CheckValue(elem) for elem in elem_seq_iter]
276    if new_values:
277      self._values.extend(new_values)
278      self._message_listener.Modified()
279
280  def MergeFrom(self, other):
281    """Appends the contents of another repeated field of the same type to this
282    one. We do not check the types of the individual fields.
283    """
284    self._values.extend(other._values)
285    self._message_listener.Modified()
286
287  def remove(self, elem):
288    """Removes an item from the list. Similar to list.remove()."""
289    self._values.remove(elem)
290    self._message_listener.Modified()
291
292  def pop(self, key=-1):
293    """Removes and returns an item at a given index. Similar to list.pop()."""
294    value = self._values[key]
295    self.__delitem__(key)
296    return value
297
298  def __setitem__(self, key, value):
299    """Sets the item on the specified position."""
300    if isinstance(key, slice):  # PY3
301      if key.step is not None:
302        raise ValueError('Extended slices not supported')
303      self.__setslice__(key.start, key.stop, value)
304    else:
305      self._values[key] = self._type_checker.CheckValue(value)
306      self._message_listener.Modified()
307
308  def __getslice__(self, start, stop):
309    """Retrieves the subset of items from between the specified indices."""
310    return self._values[start:stop]
311
312  def __setslice__(self, start, stop, values):
313    """Sets the subset of items from between the specified indices."""
314    new_values = []
315    for value in values:
316      new_values.append(self._type_checker.CheckValue(value))
317    self._values[start:stop] = new_values
318    self._message_listener.Modified()
319
320  def __delitem__(self, key):
321    """Deletes the item at the specified position."""
322    del self._values[key]
323    self._message_listener.Modified()
324
325  def __delslice__(self, start, stop):
326    """Deletes the subset of items from between the specified indices."""
327    del self._values[start:stop]
328    self._message_listener.Modified()
329
330  def __eq__(self, other):
331    """Compares the current instance with another one."""
332    if self is other:
333      return True
334    # Special case for the same type which should be common and fast.
335    if isinstance(other, self.__class__):
336      return other._values == self._values
337    # We are presumably comparing against some other sequence type.
338    return other == self._values
339
340collections.MutableSequence.register(BaseContainer)
341
342
343class RepeatedCompositeFieldContainer(BaseContainer):
344
345  """Simple, list-like container for holding repeated composite fields."""
346
347  # Disallows assignment to other attributes.
348  __slots__ = ['_message_descriptor']
349
350  def __init__(self, message_listener, message_descriptor):
351    """
352    Note that we pass in a descriptor instead of the generated directly,
353    since at the time we construct a _RepeatedCompositeFieldContainer we
354    haven't yet necessarily initialized the type that will be contained in the
355    container.
356
357    Args:
358      message_listener: A MessageListener implementation.
359        The RepeatedCompositeFieldContainer will call this object's
360        Modified() method when it is modified.
361      message_descriptor: A Descriptor instance describing the protocol type
362        that should be present in this container.  We'll use the
363        _concrete_class field of this descriptor when the client calls add().
364    """
365    super(RepeatedCompositeFieldContainer, self).__init__(message_listener)
366    self._message_descriptor = message_descriptor
367
368  def add(self, **kwargs):
369    """Adds a new element at the end of the list and returns it. Keyword
370    arguments may be used to initialize the element.
371    """
372    new_element = self._message_descriptor._concrete_class(**kwargs)
373    new_element._SetListener(self._message_listener)
374    self._values.append(new_element)
375    if not self._message_listener.dirty:
376      self._message_listener.Modified()
377    return new_element
378
379  def extend(self, elem_seq):
380    """Extends by appending the given sequence of elements of the same type
381    as this one, copying each individual message.
382    """
383    message_class = self._message_descriptor._concrete_class
384    listener = self._message_listener
385    values = self._values
386    for message in elem_seq:
387      new_element = message_class()
388      new_element._SetListener(listener)
389      new_element.MergeFrom(message)
390      values.append(new_element)
391    listener.Modified()
392
393  def MergeFrom(self, other):
394    """Appends the contents of another repeated field of the same type to this
395    one, copying each individual message.
396    """
397    self.extend(other._values)
398
399  def remove(self, elem):
400    """Removes an item from the list. Similar to list.remove()."""
401    self._values.remove(elem)
402    self._message_listener.Modified()
403
404  def pop(self, key=-1):
405    """Removes and returns an item at a given index. Similar to list.pop()."""
406    value = self._values[key]
407    self.__delitem__(key)
408    return value
409
410  def __getslice__(self, start, stop):
411    """Retrieves the subset of items from between the specified indices."""
412    return self._values[start:stop]
413
414  def __delitem__(self, key):
415    """Deletes the item at the specified position."""
416    del self._values[key]
417    self._message_listener.Modified()
418
419  def __delslice__(self, start, stop):
420    """Deletes the subset of items from between the specified indices."""
421    del self._values[start:stop]
422    self._message_listener.Modified()
423
424  def __eq__(self, other):
425    """Compares the current instance with another one."""
426    if self is other:
427      return True
428    if not isinstance(other, self.__class__):
429      raise TypeError('Can only compare repeated composite fields against '
430                      'other repeated composite fields.')
431    return self._values == other._values
432
433
434class ScalarMap(MutableMapping):
435
436  """Simple, type-checked, dict-like container for holding repeated scalars."""
437
438  # Disallows assignment to other attributes.
439  __slots__ = ['_key_checker', '_value_checker', '_values', '_message_listener']
440
441  def __init__(self, message_listener, key_checker, value_checker):
442    """
443    Args:
444      message_listener: A MessageListener implementation.
445        The ScalarMap will call this object's Modified() method when it
446        is modified.
447      key_checker: A type_checkers.ValueChecker instance to run on keys
448        inserted into this container.
449      value_checker: A type_checkers.ValueChecker instance to run on values
450        inserted into this container.
451    """
452    self._message_listener = message_listener
453    self._key_checker = key_checker
454    self._value_checker = value_checker
455    self._values = {}
456
457  def __getitem__(self, key):
458    try:
459      return self._values[key]
460    except KeyError:
461      key = self._key_checker.CheckValue(key)
462      val = self._value_checker.DefaultValue()
463      self._values[key] = val
464      return val
465
466  def __contains__(self, item):
467    # We check the key's type to match the strong-typing flavor of the API.
468    # Also this makes it easier to match the behavior of the C++ implementation.
469    self._key_checker.CheckValue(item)
470    return item in self._values
471
472  # We need to override this explicitly, because our defaultdict-like behavior
473  # will make the default implementation (from our base class) always insert
474  # the key.
475  def get(self, key, default=None):
476    if key in self:
477      return self[key]
478    else:
479      return default
480
481  def __setitem__(self, key, value):
482    checked_key = self._key_checker.CheckValue(key)
483    checked_value = self._value_checker.CheckValue(value)
484    self._values[checked_key] = checked_value
485    self._message_listener.Modified()
486
487  def __delitem__(self, key):
488    del self._values[key]
489    self._message_listener.Modified()
490
491  def __len__(self):
492    return len(self._values)
493
494  def __iter__(self):
495    return iter(self._values)
496
497  def __repr__(self):
498    return repr(self._values)
499
500  def MergeFrom(self, other):
501    self._values.update(other._values)
502    self._message_listener.Modified()
503
504  def InvalidateIterators(self):
505    # It appears that the only way to reliably invalidate iterators to
506    # self._values is to ensure that its size changes.
507    original = self._values
508    self._values = original.copy()
509    original[None] = None
510
511  # This is defined in the abstract base, but we can do it much more cheaply.
512  def clear(self):
513    self._values.clear()
514    self._message_listener.Modified()
515
516
517class MessageMap(MutableMapping):
518
519  """Simple, type-checked, dict-like container for with submessage values."""
520
521  # Disallows assignment to other attributes.
522  __slots__ = ['_key_checker', '_values', '_message_listener',
523               '_message_descriptor']
524
525  def __init__(self, message_listener, message_descriptor, key_checker):
526    """
527    Args:
528      message_listener: A MessageListener implementation.
529        The ScalarMap will call this object's Modified() method when it
530        is modified.
531      key_checker: A type_checkers.ValueChecker instance to run on keys
532        inserted into this container.
533      value_checker: A type_checkers.ValueChecker instance to run on values
534        inserted into this container.
535    """
536    self._message_listener = message_listener
537    self._message_descriptor = message_descriptor
538    self._key_checker = key_checker
539    self._values = {}
540
541  def __getitem__(self, key):
542    try:
543      return self._values[key]
544    except KeyError:
545      key = self._key_checker.CheckValue(key)
546      new_element = self._message_descriptor._concrete_class()
547      new_element._SetListener(self._message_listener)
548      self._values[key] = new_element
549      self._message_listener.Modified()
550
551      return new_element
552
553  def get_or_create(self, key):
554    """get_or_create() is an alias for getitem (ie. map[key]).
555
556    Args:
557      key: The key to get or create in the map.
558
559    This is useful in cases where you want to be explicit that the call is
560    mutating the map.  This can avoid lint errors for statements like this
561    that otherwise would appear to be pointless statements:
562
563      msg.my_map[key]
564    """
565    return self[key]
566
567  # We need to override this explicitly, because our defaultdict-like behavior
568  # will make the default implementation (from our base class) always insert
569  # the key.
570  def get(self, key, default=None):
571    if key in self:
572      return self[key]
573    else:
574      return default
575
576  def __contains__(self, item):
577    return item in self._values
578
579  def __setitem__(self, key, value):
580    raise ValueError('May not set values directly, call my_map[key].foo = 5')
581
582  def __delitem__(self, key):
583    del self._values[key]
584    self._message_listener.Modified()
585
586  def __len__(self):
587    return len(self._values)
588
589  def __iter__(self):
590    return iter(self._values)
591
592  def __repr__(self):
593    return repr(self._values)
594
595  def MergeFrom(self, other):
596    for key in other:
597      # According to documentation: "When parsing from the wire or when merging,
598      # if there are duplicate map keys the last key seen is used".
599      if key in self:
600        del self[key]
601      self[key].CopyFrom(other[key])
602    # self._message_listener.Modified() not required here, because
603    # mutations to submessages already propagate.
604
605  def InvalidateIterators(self):
606    # It appears that the only way to reliably invalidate iterators to
607    # self._values is to ensure that its size changes.
608    original = self._values
609    self._values = original.copy()
610    original[None] = None
611
612  # This is defined in the abstract base, but we can do it much more cheaply.
613  def clear(self):
614    self._values.clear()
615    self._message_listener.Modified()
616