blob: 97cdd848e34d8da4083abb512b7403686ddec91f [file] [log] [blame]
Brian Silverman9c614bc2016-02-15 20:20:02 -05001# 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 self[key].MergeFrom(other[key])
598 # self._message_listener.Modified() not required here, because
599 # mutations to submessages already propagate.
600
601 def InvalidateIterators(self):
602 # It appears that the only way to reliably invalidate iterators to
603 # self._values is to ensure that its size changes.
604 original = self._values
605 self._values = original.copy()
606 original[None] = None
607
608 # This is defined in the abstract base, but we can do it much more cheaply.
609 def clear(self):
610 self._values.clear()
611 self._message_listener.Modified()