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