OLD | NEW |
| (Empty) |
1 # Protocol Buffers - Google's data interchange format | |
2 # Copyright 2008 Google Inc. All rights reserved. | |
3 # http://code.google.com/p/protobuf/ | |
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 | |
33 This file defines container classes which represent categories of protocol | |
34 buffer field types which need extra maintenance. Currently these categories | |
35 are: | |
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 | |
44 | |
45 class BaseContainer(object): | |
46 | |
47 """Base container class.""" | |
48 | |
49 # Minimizes memory usage and disallows assignment to other attributes. | |
50 __slots__ = ['_message_listener', '_values'] | |
51 | |
52 def __init__(self, message_listener): | |
53 """ | |
54 Args: | |
55 message_listener: A MessageListener implementation. | |
56 The RepeatedScalarFieldContainer will call this object's | |
57 Modified() method when it is modified. | |
58 """ | |
59 self._message_listener = message_listener | |
60 self._values = [] | |
61 | |
62 def __getitem__(self, key): | |
63 """Retrieves item by the specified key.""" | |
64 return self._values[key] | |
65 | |
66 def __len__(self): | |
67 """Returns the number of elements in the container.""" | |
68 return len(self._values) | |
69 | |
70 def __ne__(self, other): | |
71 """Checks if another instance isn't equal to this one.""" | |
72 # The concrete classes should define __eq__. | |
73 return not self == other | |
74 | |
75 def __hash__(self): | |
76 raise TypeError('unhashable object') | |
77 | |
78 def __repr__(self): | |
79 return repr(self._values) | |
80 | |
81 def sort(self, *args, **kwargs): | |
82 # Continue to support the old sort_function keyword argument. | |
83 # This is expected to be a rare occurrence, so use LBYL to avoid | |
84 # the overhead of actually catching KeyError. | |
85 if 'sort_function' in kwargs: | |
86 kwargs['cmp'] = kwargs.pop('sort_function') | |
87 self._values.sort(*args, **kwargs) | |
88 | |
89 | |
90 class RepeatedScalarFieldContainer(BaseContainer): | |
91 | |
92 """Simple, type-checked, list-like container for holding repeated scalars.""" | |
93 | |
94 # Disallows assignment to other attributes. | |
95 __slots__ = ['_type_checker'] | |
96 | |
97 def __init__(self, message_listener, type_checker): | |
98 """ | |
99 Args: | |
100 message_listener: A MessageListener implementation. | |
101 The RepeatedScalarFieldContainer will call this object's | |
102 Modified() method when it is modified. | |
103 type_checker: A type_checkers.ValueChecker instance to run on elements | |
104 inserted into this container. | |
105 """ | |
106 super(RepeatedScalarFieldContainer, self).__init__(message_listener) | |
107 self._type_checker = type_checker | |
108 | |
109 def append(self, value): | |
110 """Appends an item to the list. Similar to list.append().""" | |
111 self._values.append(self._type_checker.CheckValue(value)) | |
112 if not self._message_listener.dirty: | |
113 self._message_listener.Modified() | |
114 | |
115 def insert(self, key, value): | |
116 """Inserts the item at the specified position. Similar to list.insert().""" | |
117 self._values.insert(key, self._type_checker.CheckValue(value)) | |
118 if not self._message_listener.dirty: | |
119 self._message_listener.Modified() | |
120 | |
121 def extend(self, elem_seq): | |
122 """Extends by appending the given sequence. Similar to list.extend().""" | |
123 if not elem_seq: | |
124 return | |
125 | |
126 new_values = [] | |
127 for elem in elem_seq: | |
128 new_values.append(self._type_checker.CheckValue(elem)) | |
129 self._values.extend(new_values) | |
130 self._message_listener.Modified() | |
131 | |
132 def MergeFrom(self, other): | |
133 """Appends the contents of another repeated field of the same type to this | |
134 one. We do not check the types of the individual fields. | |
135 """ | |
136 self._values.extend(other._values) | |
137 self._message_listener.Modified() | |
138 | |
139 def remove(self, elem): | |
140 """Removes an item from the list. Similar to list.remove().""" | |
141 self._values.remove(elem) | |
142 self._message_listener.Modified() | |
143 | |
144 def __setitem__(self, key, value): | |
145 """Sets the item on the specified position.""" | |
146 if isinstance(key, slice): # PY3 | |
147 if key.step is not None: | |
148 raise ValueError('Extended slices not supported') | |
149 self.__setslice__(key.start, key.stop, value) | |
150 else: | |
151 self._values[key] = self._type_checker.CheckValue(value) | |
152 self._message_listener.Modified() | |
153 | |
154 def __getslice__(self, start, stop): | |
155 """Retrieves the subset of items from between the specified indices.""" | |
156 return self._values[start:stop] | |
157 | |
158 def __setslice__(self, start, stop, values): | |
159 """Sets the subset of items from between the specified indices.""" | |
160 new_values = [] | |
161 for value in values: | |
162 new_values.append(self._type_checker.CheckValue(value)) | |
163 self._values[start:stop] = new_values | |
164 self._message_listener.Modified() | |
165 | |
166 def __delitem__(self, key): | |
167 """Deletes the item at the specified position.""" | |
168 del self._values[key] | |
169 self._message_listener.Modified() | |
170 | |
171 def __delslice__(self, start, stop): | |
172 """Deletes the subset of items from between the specified indices.""" | |
173 del self._values[start:stop] | |
174 self._message_listener.Modified() | |
175 | |
176 def __eq__(self, other): | |
177 """Compares the current instance with another one.""" | |
178 if self is other: | |
179 return True | |
180 # Special case for the same type which should be common and fast. | |
181 if isinstance(other, self.__class__): | |
182 return other._values == self._values | |
183 # We are presumably comparing against some other sequence type. | |
184 return other == self._values | |
185 | |
186 | |
187 class RepeatedCompositeFieldContainer(BaseContainer): | |
188 | |
189 """Simple, list-like container for holding repeated composite fields.""" | |
190 | |
191 # Disallows assignment to other attributes. | |
192 __slots__ = ['_message_descriptor'] | |
193 | |
194 def __init__(self, message_listener, message_descriptor): | |
195 """ | |
196 Note that we pass in a descriptor instead of the generated directly, | |
197 since at the time we construct a _RepeatedCompositeFieldContainer we | |
198 haven't yet necessarily initialized the type that will be contained in the | |
199 container. | |
200 | |
201 Args: | |
202 message_listener: A MessageListener implementation. | |
203 The RepeatedCompositeFieldContainer will call this object's | |
204 Modified() method when it is modified. | |
205 message_descriptor: A Descriptor instance describing the protocol type | |
206 that should be present in this container. We'll use the | |
207 _concrete_class field of this descriptor when the client calls add(). | |
208 """ | |
209 super(RepeatedCompositeFieldContainer, self).__init__(message_listener) | |
210 self._message_descriptor = message_descriptor | |
211 | |
212 def add(self, **kwargs): | |
213 """Adds a new element at the end of the list and returns it. Keyword | |
214 arguments may be used to initialize the element. | |
215 """ | |
216 new_element = self._message_descriptor._concrete_class(**kwargs) | |
217 new_element._SetListener(self._message_listener) | |
218 self._values.append(new_element) | |
219 if not self._message_listener.dirty: | |
220 self._message_listener.Modified() | |
221 return new_element | |
222 | |
223 def extend(self, elem_seq): | |
224 """Extends by appending the given sequence of elements of the same type | |
225 as this one, copying each individual message. | |
226 """ | |
227 message_class = self._message_descriptor._concrete_class | |
228 listener = self._message_listener | |
229 values = self._values | |
230 for message in elem_seq: | |
231 new_element = message_class() | |
232 new_element._SetListener(listener) | |
233 new_element.MergeFrom(message) | |
234 values.append(new_element) | |
235 listener.Modified() | |
236 | |
237 def MergeFrom(self, other): | |
238 """Appends the contents of another repeated field of the same type to this | |
239 one, copying each individual message. | |
240 """ | |
241 self.extend(other._values) | |
242 | |
243 def remove(self, elem): | |
244 """Removes an item from the list. Similar to list.remove().""" | |
245 self._values.remove(elem) | |
246 self._message_listener.Modified() | |
247 | |
248 def __getslice__(self, start, stop): | |
249 """Retrieves the subset of items from between the specified indices.""" | |
250 return self._values[start:stop] | |
251 | |
252 def __delitem__(self, key): | |
253 """Deletes the item at the specified position.""" | |
254 del self._values[key] | |
255 self._message_listener.Modified() | |
256 | |
257 def __delslice__(self, start, stop): | |
258 """Deletes the subset of items from between the specified indices.""" | |
259 del self._values[start:stop] | |
260 self._message_listener.Modified() | |
261 | |
262 def __eq__(self, other): | |
263 """Compares the current instance with another one.""" | |
264 if self is other: | |
265 return True | |
266 if not isinstance(other, self.__class__): | |
267 raise TypeError('Can only compare repeated composite fields against ' | |
268 'other repeated composite fields.') | |
269 return self._values == other._values | |
OLD | NEW |