Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(8)

Side by Side Diff: third_party/protobuf/python/google/protobuf/text_format.py

Issue 1291903002: Pull new version of protobuf sources. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Created 5 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 # Protocol Buffers - Google's data interchange format 1 # Protocol Buffers - Google's data interchange format
2 # Copyright 2008 Google Inc. All rights reserved. 2 # Copyright 2008 Google Inc. All rights reserved.
3 # http://code.google.com/p/protobuf/ 3 # https://developers.google.com/protocol-buffers/
4 # 4 #
5 # Redistribution and use in source and binary forms, with or without 5 # Redistribution and use in source and binary forms, with or without
6 # modification, are permitted provided that the following conditions are 6 # modification, are permitted provided that the following conditions are
7 # met: 7 # met:
8 # 8 #
9 # * Redistributions of source code must retain the above copyright 9 # * Redistributions of source code must retain the above copyright
10 # notice, this list of conditions and the following disclaimer. 10 # notice, this list of conditions and the following disclaimer.
11 # * Redistributions in binary form must reproduce the above 11 # * Redistributions in binary form must reproduce the above
12 # copyright notice, this list of conditions and the following disclaimer 12 # copyright notice, this list of conditions and the following disclaimer
13 # in the documentation and/or other materials provided with the 13 # in the documentation and/or other materials provided with the
14 # distribution. 14 # distribution.
15 # * Neither the name of Google Inc. nor the names of its 15 # * Neither the name of Google Inc. nor the names of its
16 # contributors may be used to endorse or promote products derived from 16 # contributors may be used to endorse or promote products derived from
17 # this software without specific prior written permission. 17 # this software without specific prior written permission.
18 # 18 #
19 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 19 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20 # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 20 # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21 # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 21 # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22 # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 22 # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
23 # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 23 # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24 # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 24 # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25 # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 25 # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26 # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 26 # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27 # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 27 # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 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. 29 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 30
31 #PY25 compatible for GAE.
32 #
33 # Copyright 2007 Google Inc. All Rights Reserved.
34
31 """Contains routines for printing protocol messages in text format.""" 35 """Contains routines for printing protocol messages in text format."""
32 36
33 __author__ = 'kenton@google.com (Kenton Varda)' 37 __author__ = 'kenton@google.com (Kenton Varda)'
34 38
35 import cStringIO 39 import cStringIO
36 import re 40 import re
37 41
38 from collections import deque
39 from google.protobuf.internal import type_checkers 42 from google.protobuf.internal import type_checkers
40 from google.protobuf import descriptor 43 from google.protobuf import descriptor
44 from google.protobuf import text_encoding
41 45
42 __all__ = [ 'MessageToString', 'PrintMessage', 'PrintField', 46 __all__ = ['MessageToString', 'PrintMessage', 'PrintField',
43 'PrintFieldValue', 'Merge' ] 47 'PrintFieldValue', 'Merge']
44 48
45 49
46 _INTEGER_CHECKERS = (type_checkers.Uint32ValueChecker(), 50 _INTEGER_CHECKERS = (type_checkers.Uint32ValueChecker(),
47 type_checkers.Int32ValueChecker(), 51 type_checkers.Int32ValueChecker(),
48 type_checkers.Uint64ValueChecker(), 52 type_checkers.Uint64ValueChecker(),
49 type_checkers.Int64ValueChecker()) 53 type_checkers.Int64ValueChecker())
50 _FLOAT_INFINITY = re.compile('-?inf(?:inity)?f?', re.IGNORECASE) 54 _FLOAT_INFINITY = re.compile('-?inf(?:inity)?f?', re.IGNORECASE)
51 _FLOAT_NAN = re.compile('nanf?', re.IGNORECASE) 55 _FLOAT_NAN = re.compile('nanf?', re.IGNORECASE)
56 _FLOAT_TYPES = frozenset([descriptor.FieldDescriptor.CPPTYPE_FLOAT,
57 descriptor.FieldDescriptor.CPPTYPE_DOUBLE])
52 58
53 59
54 class ParseError(Exception): 60 class Error(Exception):
61 """Top-level module error for text_format."""
62
63
64 class ParseError(Error):
55 """Thrown in case of ASCII parsing error.""" 65 """Thrown in case of ASCII parsing error."""
56 66
57 67
58 def MessageToString(message, as_utf8=False, as_one_line=False): 68 def MessageToString(message, as_utf8=False, as_one_line=False,
69 pointy_brackets=False, use_index_order=False,
70 float_format=None):
71 """Convert protobuf message to text format.
72
73 Floating point values can be formatted compactly with 15 digits of
74 precision (which is the most that IEEE 754 "double" can guarantee)
75 using float_format='.15g'.
76
77 Args:
78 message: The protocol buffers message.
79 as_utf8: Produce text output in UTF8 format.
80 as_one_line: Don't introduce newlines between fields.
81 pointy_brackets: If True, use angle brackets instead of curly braces for
82 nesting.
83 use_index_order: If True, print fields of a proto message using the order
84 defined in source code instead of the field number. By default, use the
85 field number order.
86 float_format: If set, use this to specify floating point number formatting
87 (per the "Format Specification Mini-Language"); otherwise, str() is used.
88
89 Returns:
90 A string of the text formatted protocol buffer message.
91 """
59 out = cStringIO.StringIO() 92 out = cStringIO.StringIO()
60 PrintMessage(message, out, as_utf8=as_utf8, as_one_line=as_one_line) 93 PrintMessage(message, out, as_utf8=as_utf8, as_one_line=as_one_line,
94 pointy_brackets=pointy_brackets,
95 use_index_order=use_index_order,
96 float_format=float_format)
61 result = out.getvalue() 97 result = out.getvalue()
62 out.close() 98 out.close()
63 if as_one_line: 99 if as_one_line:
64 return result.rstrip() 100 return result.rstrip()
65 return result 101 return result
66 102
103 def _IsMapEntry(field):
104 return (field.type == descriptor.FieldDescriptor.TYPE_MESSAGE and
105 field.message_type.has_options and
106 field.message_type.GetOptions().map_entry)
67 107
68 def PrintMessage(message, out, indent=0, as_utf8=False, as_one_line=False): 108 def PrintMessage(message, out, indent=0, as_utf8=False, as_one_line=False,
69 for field, value in message.ListFields(): 109 pointy_brackets=False, use_index_order=False,
70 if field.label == descriptor.FieldDescriptor.LABEL_REPEATED: 110 float_format=None):
111 fields = message.ListFields()
112 if use_index_order:
113 fields.sort(key=lambda x: x[0].index)
114 for field, value in fields:
115 if _IsMapEntry(field):
116 for key in value:
117 # This is slow for maps with submessage entires because it copies the
118 # entire tree. Unfortunately this would take significant refactoring
119 # of this file to work around.
120 #
121 # TODO(haberman): refactor and optimize if this becomes an issue.
122 entry_submsg = field.message_type._concrete_class(
123 key=key, value=value[key])
124 PrintField(field, entry_submsg, out, indent, as_utf8, as_one_line,
125 pointy_brackets=pointy_brackets,
126 use_index_order=use_index_order, float_format=float_format)
127 elif field.label == descriptor.FieldDescriptor.LABEL_REPEATED:
71 for element in value: 128 for element in value:
72 PrintField(field, element, out, indent, as_utf8, as_one_line) 129 PrintField(field, element, out, indent, as_utf8, as_one_line,
130 pointy_brackets=pointy_brackets,
131 use_index_order=use_index_order,
132 float_format=float_format)
73 else: 133 else:
74 PrintField(field, value, out, indent, as_utf8, as_one_line) 134 PrintField(field, value, out, indent, as_utf8, as_one_line,
135 pointy_brackets=pointy_brackets,
136 use_index_order=use_index_order,
137 float_format=float_format)
75 138
76 139
77 def PrintField(field, value, out, indent=0, as_utf8=False, as_one_line=False): 140 def PrintField(field, value, out, indent=0, as_utf8=False, as_one_line=False,
141 pointy_brackets=False, use_index_order=False, float_format=None):
78 """Print a single field name/value pair. For repeated fields, the value 142 """Print a single field name/value pair. For repeated fields, the value
79 should be a single element.""" 143 should be a single element."""
80 144
81 out.write(' ' * indent); 145 out.write(' ' * indent)
82 if field.is_extension: 146 if field.is_extension:
83 out.write('[') 147 out.write('[')
84 if (field.containing_type.GetOptions().message_set_wire_format and 148 if (field.containing_type.GetOptions().message_set_wire_format and
85 field.type == descriptor.FieldDescriptor.TYPE_MESSAGE and 149 field.type == descriptor.FieldDescriptor.TYPE_MESSAGE and
86 field.message_type == field.extension_scope and 150 field.message_type == field.extension_scope and
87 field.label == descriptor.FieldDescriptor.LABEL_OPTIONAL): 151 field.label == descriptor.FieldDescriptor.LABEL_OPTIONAL):
88 out.write(field.message_type.full_name) 152 out.write(field.message_type.full_name)
89 else: 153 else:
90 out.write(field.full_name) 154 out.write(field.full_name)
91 out.write(']') 155 out.write(']')
92 elif field.type == descriptor.FieldDescriptor.TYPE_GROUP: 156 elif field.type == descriptor.FieldDescriptor.TYPE_GROUP:
93 # For groups, use the capitalized name. 157 # For groups, use the capitalized name.
94 out.write(field.message_type.name) 158 out.write(field.message_type.name)
95 else: 159 else:
96 out.write(field.name) 160 out.write(field.name)
97 161
98 if field.cpp_type != descriptor.FieldDescriptor.CPPTYPE_MESSAGE: 162 if field.cpp_type != descriptor.FieldDescriptor.CPPTYPE_MESSAGE:
99 # The colon is optional in this case, but our cross-language golden files 163 # The colon is optional in this case, but our cross-language golden files
100 # don't include it. 164 # don't include it.
101 out.write(': ') 165 out.write(': ')
102 166
103 PrintFieldValue(field, value, out, indent, as_utf8, as_one_line) 167 PrintFieldValue(field, value, out, indent, as_utf8, as_one_line,
168 pointy_brackets=pointy_brackets,
169 use_index_order=use_index_order,
170 float_format=float_format)
104 if as_one_line: 171 if as_one_line:
105 out.write(' ') 172 out.write(' ')
106 else: 173 else:
107 out.write('\n') 174 out.write('\n')
108 175
109 176
110 def PrintFieldValue(field, value, out, indent=0, 177 def PrintFieldValue(field, value, out, indent=0, as_utf8=False,
111 as_utf8=False, as_one_line=False): 178 as_one_line=False, pointy_brackets=False,
179 use_index_order=False,
180 float_format=None):
112 """Print a single field value (not including name). For repeated fields, 181 """Print a single field value (not including name). For repeated fields,
113 the value should be a single element.""" 182 the value should be a single element."""
114 183
184 if pointy_brackets:
185 openb = '<'
186 closeb = '>'
187 else:
188 openb = '{'
189 closeb = '}'
190
115 if field.cpp_type == descriptor.FieldDescriptor.CPPTYPE_MESSAGE: 191 if field.cpp_type == descriptor.FieldDescriptor.CPPTYPE_MESSAGE:
116 if as_one_line: 192 if as_one_line:
117 out.write(' { ') 193 out.write(' %s ' % openb)
118 PrintMessage(value, out, indent, as_utf8, as_one_line) 194 PrintMessage(value, out, indent, as_utf8, as_one_line,
119 out.write('}') 195 pointy_brackets=pointy_brackets,
196 use_index_order=use_index_order,
197 float_format=float_format)
198 out.write(closeb)
120 else: 199 else:
121 out.write(' {\n') 200 out.write(' %s\n' % openb)
122 PrintMessage(value, out, indent + 2, as_utf8, as_one_line) 201 PrintMessage(value, out, indent + 2, as_utf8, as_one_line,
123 out.write(' ' * indent + '}') 202 pointy_brackets=pointy_brackets,
203 use_index_order=use_index_order,
204 float_format=float_format)
205 out.write(' ' * indent + closeb)
124 elif field.cpp_type == descriptor.FieldDescriptor.CPPTYPE_ENUM: 206 elif field.cpp_type == descriptor.FieldDescriptor.CPPTYPE_ENUM:
125 enum_value = field.enum_type.values_by_number.get(value, None) 207 enum_value = field.enum_type.values_by_number.get(value, None)
126 if enum_value is not None: 208 if enum_value is not None:
127 out.write(enum_value.name) 209 out.write(enum_value.name)
128 else: 210 else:
129 out.write(str(value)) 211 out.write(str(value))
130 elif field.cpp_type == descriptor.FieldDescriptor.CPPTYPE_STRING: 212 elif field.cpp_type == descriptor.FieldDescriptor.CPPTYPE_STRING:
131 out.write('\"') 213 out.write('\"')
132 if type(value) is unicode: 214 if isinstance(value, unicode):
133 out.write(_CEscape(value.encode('utf-8'), as_utf8)) 215 out_value = value.encode('utf-8')
134 else: 216 else:
135 out.write(_CEscape(value, as_utf8)) 217 out_value = value
218 if field.type == descriptor.FieldDescriptor.TYPE_BYTES:
219 # We need to escape non-UTF8 chars in TYPE_BYTES field.
220 out_as_utf8 = False
221 else:
222 out_as_utf8 = as_utf8
223 out.write(text_encoding.CEscape(out_value, out_as_utf8))
136 out.write('\"') 224 out.write('\"')
137 elif field.cpp_type == descriptor.FieldDescriptor.CPPTYPE_BOOL: 225 elif field.cpp_type == descriptor.FieldDescriptor.CPPTYPE_BOOL:
138 if value: 226 if value:
139 out.write("true") 227 out.write('true')
140 else: 228 else:
141 out.write("false") 229 out.write('false')
230 elif field.cpp_type in _FLOAT_TYPES and float_format is not None:
231 out.write('{1:{0}}'.format(float_format, value))
142 else: 232 else:
143 out.write(str(value)) 233 out.write(str(value))
144 234
145 235
146 def Merge(text, message): 236 def Parse(text, message):
147 """Merges an ASCII representation of a protocol message into a message. 237 """Parses an ASCII representation of a protocol message into a message.
148 238
149 Args: 239 Args:
150 text: Message ASCII representation. 240 text: Message ASCII representation.
151 message: A protocol buffer message to merge into. 241 message: A protocol buffer message to merge into.
152 242
243 Returns:
244 The same message passed as argument.
245
153 Raises: 246 Raises:
154 ParseError: On ASCII parsing problems. 247 ParseError: On ASCII parsing problems.
155 """ 248 """
156 tokenizer = _Tokenizer(text) 249 if not isinstance(text, str): text = text.decode('utf-8')
157 while not tokenizer.AtEnd(): 250 return ParseLines(text.split('\n'), message)
158 _MergeField(tokenizer, message)
159 251
160 252
161 def _MergeField(tokenizer, message): 253 def Merge(text, message):
254 """Parses an ASCII representation of a protocol message into a message.
255
256 Like Parse(), but allows repeated values for a non-repeated field, and uses
257 the last one.
258
259 Args:
260 text: Message ASCII representation.
261 message: A protocol buffer message to merge into.
262
263 Returns:
264 The same message passed as argument.
265
266 Raises:
267 ParseError: On ASCII parsing problems.
268 """
269 return MergeLines(text.split('\n'), message)
270
271
272 def ParseLines(lines, message):
273 """Parses an ASCII representation of a protocol message into a message.
274
275 Args:
276 lines: An iterable of lines of a message's ASCII representation.
277 message: A protocol buffer message to merge into.
278
279 Returns:
280 The same message passed as argument.
281
282 Raises:
283 ParseError: On ASCII parsing problems.
284 """
285 _ParseOrMerge(lines, message, False)
286 return message
287
288
289 def MergeLines(lines, message):
290 """Parses an ASCII representation of a protocol message into a message.
291
292 Args:
293 lines: An iterable of lines of a message's ASCII representation.
294 message: A protocol buffer message to merge into.
295
296 Returns:
297 The same message passed as argument.
298
299 Raises:
300 ParseError: On ASCII parsing problems.
301 """
302 _ParseOrMerge(lines, message, True)
303 return message
304
305
306 def _ParseOrMerge(lines, message, allow_multiple_scalars):
307 """Converts an ASCII representation of a protocol message into a message.
308
309 Args:
310 lines: Lines of a message's ASCII representation.
311 message: A protocol buffer message to merge into.
312 allow_multiple_scalars: Determines if repeated values for a non-repeated
313 field are permitted, e.g., the string "foo: 1 foo: 2" for a
314 required/optional field named "foo".
315
316 Raises:
317 ParseError: On ASCII parsing problems.
318 """
319 tokenizer = _Tokenizer(lines)
320 while not tokenizer.AtEnd():
321 _MergeField(tokenizer, message, allow_multiple_scalars)
322
323
324 def _MergeField(tokenizer, message, allow_multiple_scalars):
162 """Merges a single protocol message field into a message. 325 """Merges a single protocol message field into a message.
163 326
164 Args: 327 Args:
165 tokenizer: A tokenizer to parse the field name and values. 328 tokenizer: A tokenizer to parse the field name and values.
166 message: A protocol message to record the data. 329 message: A protocol message to record the data.
330 allow_multiple_scalars: Determines if repeated values for a non-repeated
331 field are permitted, e.g., the string "foo: 1 foo: 2" for a
332 required/optional field named "foo".
167 333
168 Raises: 334 Raises:
169 ParseError: In case of ASCII parsing problems. 335 ParseError: In case of ASCII parsing problems.
170 """ 336 """
171 message_descriptor = message.DESCRIPTOR 337 message_descriptor = message.DESCRIPTOR
338 if (hasattr(message_descriptor, 'syntax') and
339 message_descriptor.syntax == 'proto3'):
340 # Proto3 doesn't represent presence so we can't test if multiple
341 # scalars have occurred. We have to allow them.
342 allow_multiple_scalars = True
172 if tokenizer.TryConsume('['): 343 if tokenizer.TryConsume('['):
173 name = [tokenizer.ConsumeIdentifier()] 344 name = [tokenizer.ConsumeIdentifier()]
174 while tokenizer.TryConsume('.'): 345 while tokenizer.TryConsume('.'):
175 name.append(tokenizer.ConsumeIdentifier()) 346 name.append(tokenizer.ConsumeIdentifier())
176 name = '.'.join(name) 347 name = '.'.join(name)
177 348
178 if not message_descriptor.is_extendable: 349 if not message_descriptor.is_extendable:
179 raise tokenizer.ParseErrorPreviousToken( 350 raise tokenizer.ParseErrorPreviousToken(
180 'Message type "%s" does not have extensions.' % 351 'Message type "%s" does not have extensions.' %
181 message_descriptor.full_name) 352 message_descriptor.full_name)
353 # pylint: disable=protected-access
182 field = message.Extensions._FindExtensionByName(name) 354 field = message.Extensions._FindExtensionByName(name)
355 # pylint: enable=protected-access
183 if not field: 356 if not field:
184 raise tokenizer.ParseErrorPreviousToken( 357 raise tokenizer.ParseErrorPreviousToken(
185 'Extension "%s" not registered.' % name) 358 'Extension "%s" not registered.' % name)
186 elif message_descriptor != field.containing_type: 359 elif message_descriptor != field.containing_type:
187 raise tokenizer.ParseErrorPreviousToken( 360 raise tokenizer.ParseErrorPreviousToken(
188 'Extension "%s" does not extend message type "%s".' % ( 361 'Extension "%s" does not extend message type "%s".' % (
189 name, message_descriptor.full_name)) 362 name, message_descriptor.full_name))
190 tokenizer.Consume(']') 363 tokenizer.Consume(']')
191 else: 364 else:
192 name = tokenizer.ConsumeIdentifier() 365 name = tokenizer.ConsumeIdentifier()
(...skipping 10 matching lines...) Expand all
203 if (field and field.type == descriptor.FieldDescriptor.TYPE_GROUP and 376 if (field and field.type == descriptor.FieldDescriptor.TYPE_GROUP and
204 field.message_type.name != name): 377 field.message_type.name != name):
205 field = None 378 field = None
206 379
207 if not field: 380 if not field:
208 raise tokenizer.ParseErrorPreviousToken( 381 raise tokenizer.ParseErrorPreviousToken(
209 'Message type "%s" has no field named "%s".' % ( 382 'Message type "%s" has no field named "%s".' % (
210 message_descriptor.full_name, name)) 383 message_descriptor.full_name, name))
211 384
212 if field.cpp_type == descriptor.FieldDescriptor.CPPTYPE_MESSAGE: 385 if field.cpp_type == descriptor.FieldDescriptor.CPPTYPE_MESSAGE:
386 is_map_entry = _IsMapEntry(field)
213 tokenizer.TryConsume(':') 387 tokenizer.TryConsume(':')
214 388
215 if tokenizer.TryConsume('<'): 389 if tokenizer.TryConsume('<'):
216 end_token = '>' 390 end_token = '>'
217 else: 391 else:
218 tokenizer.Consume('{') 392 tokenizer.Consume('{')
219 end_token = '}' 393 end_token = '}'
220 394
221 if field.label == descriptor.FieldDescriptor.LABEL_REPEATED: 395 if field.label == descriptor.FieldDescriptor.LABEL_REPEATED:
222 if field.is_extension: 396 if field.is_extension:
223 sub_message = message.Extensions[field].add() 397 sub_message = message.Extensions[field].add()
398 elif is_map_entry:
399 sub_message = field.message_type._concrete_class()
224 else: 400 else:
225 sub_message = getattr(message, field.name).add() 401 sub_message = getattr(message, field.name).add()
226 else: 402 else:
227 if field.is_extension: 403 if field.is_extension:
228 sub_message = message.Extensions[field] 404 sub_message = message.Extensions[field]
229 else: 405 else:
230 sub_message = getattr(message, field.name) 406 sub_message = getattr(message, field.name)
231 sub_message.SetInParent() 407 sub_message.SetInParent()
232 408
233 while not tokenizer.TryConsume(end_token): 409 while not tokenizer.TryConsume(end_token):
234 if tokenizer.AtEnd(): 410 if tokenizer.AtEnd():
235 raise tokenizer.ParseErrorPreviousToken('Expected "%s".' % (end_token)) 411 raise tokenizer.ParseErrorPreviousToken('Expected "%s".' % (end_token))
236 _MergeField(tokenizer, sub_message) 412 _MergeField(tokenizer, sub_message, allow_multiple_scalars)
413
414 if is_map_entry:
415 value_cpptype = field.message_type.fields_by_name['value'].cpp_type
416 if value_cpptype == descriptor.FieldDescriptor.CPPTYPE_MESSAGE:
417 value = getattr(message, field.name)[sub_message.key]
418 value.MergeFrom(sub_message.value)
419 else:
420 getattr(message, field.name)[sub_message.key] = sub_message.value
237 else: 421 else:
238 _MergeScalarField(tokenizer, message, field) 422 _MergeScalarField(tokenizer, message, field, allow_multiple_scalars)
423
424 # For historical reasons, fields may optionally be separated by commas or
425 # semicolons.
426 if not tokenizer.TryConsume(','):
427 tokenizer.TryConsume(';')
239 428
240 429
241 def _MergeScalarField(tokenizer, message, field): 430 def _MergeScalarField(tokenizer, message, field, allow_multiple_scalars):
242 """Merges a single protocol message scalar field into a message. 431 """Merges a single protocol message scalar field into a message.
243 432
244 Args: 433 Args:
245 tokenizer: A tokenizer to parse the field value. 434 tokenizer: A tokenizer to parse the field value.
246 message: A protocol message to record the data. 435 message: A protocol message to record the data.
247 field: The descriptor of the field to be merged. 436 field: The descriptor of the field to be merged.
437 allow_multiple_scalars: Determines if repeated values for a non-repeated
438 field are permitted, e.g., the string "foo: 1 foo: 2" for a
439 required/optional field named "foo".
248 440
249 Raises: 441 Raises:
250 ParseError: In case of ASCII parsing problems. 442 ParseError: In case of ASCII parsing problems.
251 RuntimeError: On runtime errors. 443 RuntimeError: On runtime errors.
252 """ 444 """
253 tokenizer.Consume(':') 445 tokenizer.Consume(':')
254 value = None 446 value = None
255 447
256 if field.type in (descriptor.FieldDescriptor.TYPE_INT32, 448 if field.type in (descriptor.FieldDescriptor.TYPE_INT32,
257 descriptor.FieldDescriptor.TYPE_SINT32, 449 descriptor.FieldDescriptor.TYPE_SINT32,
(...skipping 23 matching lines...) Expand all
281 else: 473 else:
282 raise RuntimeError('Unknown field type %d' % field.type) 474 raise RuntimeError('Unknown field type %d' % field.type)
283 475
284 if field.label == descriptor.FieldDescriptor.LABEL_REPEATED: 476 if field.label == descriptor.FieldDescriptor.LABEL_REPEATED:
285 if field.is_extension: 477 if field.is_extension:
286 message.Extensions[field].append(value) 478 message.Extensions[field].append(value)
287 else: 479 else:
288 getattr(message, field.name).append(value) 480 getattr(message, field.name).append(value)
289 else: 481 else:
290 if field.is_extension: 482 if field.is_extension:
291 message.Extensions[field] = value 483 if not allow_multiple_scalars and message.HasExtension(field):
484 raise tokenizer.ParseErrorPreviousToken(
485 'Message type "%s" should not have multiple "%s" extensions.' %
486 (message.DESCRIPTOR.full_name, field.full_name))
487 else:
488 message.Extensions[field] = value
292 else: 489 else:
293 setattr(message, field.name, value) 490 if not allow_multiple_scalars and message.HasField(field.name):
491 raise tokenizer.ParseErrorPreviousToken(
492 'Message type "%s" should not have multiple "%s" fields.' %
493 (message.DESCRIPTOR.full_name, field.name))
494 else:
495 setattr(message, field.name, value)
294 496
295 497
296 class _Tokenizer(object): 498 class _Tokenizer(object):
297 """Protocol buffer ASCII representation tokenizer. 499 """Protocol buffer ASCII representation tokenizer.
298 500
299 This class handles the lower level string parsing by splitting it into 501 This class handles the lower level string parsing by splitting it into
300 meaningful tokens. 502 meaningful tokens.
301 503
302 It was directly ported from the Java protocol buffer API. 504 It was directly ported from the Java protocol buffer API.
303 """ 505 """
304 506
305 _WHITESPACE = re.compile('(\\s|(#.*$))+', re.MULTILINE) 507 _WHITESPACE = re.compile('(\\s|(#.*$))+', re.MULTILINE)
306 _TOKEN = re.compile( 508 _TOKEN = re.compile(
307 '[a-zA-Z_][0-9a-zA-Z_+-]*|' # an identifier 509 '[a-zA-Z_][0-9a-zA-Z_+-]*|' # an identifier
308 '[0-9+-][0-9a-zA-Z_.+-]*|' # a number 510 '[0-9+-][0-9a-zA-Z_.+-]*|' # a number
309 '\"([^\"\n\\\\]|\\\\.)*(\"|\\\\?$)|' # a double-quoted string 511 '\"([^\"\n\\\\]|\\\\.)*(\"|\\\\?$)|' # a double-quoted string
310 '\'([^\'\n\\\\]|\\\\.)*(\'|\\\\?$)') # a single-quoted string 512 '\'([^\'\n\\\\]|\\\\.)*(\'|\\\\?$)') # a single-quoted string
311 _IDENTIFIER = re.compile('\w+') 513 _IDENTIFIER = re.compile(r'\w+')
312 514
313 def __init__(self, text_message): 515 def __init__(self, lines):
314 self._text_message = text_message
315
316 self._position = 0 516 self._position = 0
317 self._line = -1 517 self._line = -1
318 self._column = 0 518 self._column = 0
319 self._token_start = None 519 self._token_start = None
320 self.token = '' 520 self.token = ''
321 self._lines = deque(text_message.split('\n')) 521 self._lines = iter(lines)
322 self._current_line = '' 522 self._current_line = ''
323 self._previous_line = 0 523 self._previous_line = 0
324 self._previous_column = 0 524 self._previous_column = 0
525 self._more_lines = True
325 self._SkipWhitespace() 526 self._SkipWhitespace()
326 self.NextToken() 527 self.NextToken()
327 528
328 def AtEnd(self): 529 def AtEnd(self):
329 """Checks the end of the text was reached. 530 """Checks the end of the text was reached.
330 531
331 Returns: 532 Returns:
332 True iff the end was reached. 533 True iff the end was reached.
333 """ 534 """
334 return self.token == '' 535 return not self.token
335 536
336 def _PopLine(self): 537 def _PopLine(self):
337 while len(self._current_line) <= self._column: 538 while len(self._current_line) <= self._column:
338 if not self._lines: 539 try:
540 self._current_line = self._lines.next()
541 except StopIteration:
339 self._current_line = '' 542 self._current_line = ''
543 self._more_lines = False
340 return 544 return
341 self._line += 1 545 else:
342 self._column = 0 546 self._line += 1
343 self._current_line = self._lines.popleft() 547 self._column = 0
344 548
345 def _SkipWhitespace(self): 549 def _SkipWhitespace(self):
346 while True: 550 while True:
347 self._PopLine() 551 self._PopLine()
348 match = self._WHITESPACE.match(self._current_line, self._column) 552 match = self._WHITESPACE.match(self._current_line, self._column)
349 if not match: 553 if not match:
350 break 554 break
351 length = len(match.group(0)) 555 length = len(match.group(0))
352 self._column += length 556 self._column += length
353 557
(...skipping 136 matching lines...) Expand 10 before | Expand all | Expand 10 after
490 694
491 def ConsumeString(self): 695 def ConsumeString(self):
492 """Consumes a string value. 696 """Consumes a string value.
493 697
494 Returns: 698 Returns:
495 The string parsed. 699 The string parsed.
496 700
497 Raises: 701 Raises:
498 ParseError: If a string value couldn't be consumed. 702 ParseError: If a string value couldn't be consumed.
499 """ 703 """
500 bytes = self.ConsumeByteString() 704 the_bytes = self.ConsumeByteString()
501 try: 705 try:
502 return unicode(bytes, 'utf-8') 706 return unicode(the_bytes, 'utf-8')
503 except UnicodeDecodeError, e: 707 except UnicodeDecodeError, e:
504 raise self._StringParseError(e) 708 raise self._StringParseError(e)
505 709
506 def ConsumeByteString(self): 710 def ConsumeByteString(self):
507 """Consumes a byte array value. 711 """Consumes a byte array value.
508 712
509 Returns: 713 Returns:
510 The array parsed (as a string). 714 The array parsed (as a string).
511 715
512 Raises: 716 Raises:
513 ParseError: If a byte array value couldn't be consumed. 717 ParseError: If a byte array value couldn't be consumed.
514 """ 718 """
515 list = [self._ConsumeSingleByteString()] 719 the_list = [self._ConsumeSingleByteString()]
516 while len(self.token) > 0 and self.token[0] in ('\'', '"'): 720 while self.token and self.token[0] in ('\'', '"'):
517 list.append(self._ConsumeSingleByteString()) 721 the_list.append(self._ConsumeSingleByteString())
518 return "".join(list) 722 return ''.encode('latin1').join(the_list) ##PY25
723 ##!PY25 return b''.join(the_list)
519 724
520 def _ConsumeSingleByteString(self): 725 def _ConsumeSingleByteString(self):
521 """Consume one token of a string literal. 726 """Consume one token of a string literal.
522 727
523 String literals (whether bytes or text) can come in multiple adjacent 728 String literals (whether bytes or text) can come in multiple adjacent
524 tokens which are automatically concatenated, like in C or Python. This 729 tokens which are automatically concatenated, like in C or Python. This
525 method only consumes one token. 730 method only consumes one token.
731
732 Raises:
733 ParseError: When the wrong format data is found.
526 """ 734 """
527 text = self.token 735 text = self.token
528 if len(text) < 1 or text[0] not in ('\'', '"'): 736 if len(text) < 1 or text[0] not in ('\'', '"'):
529 raise self._ParseError('Expected string.') 737 raise self._ParseError('Expected string but found: %r' % (text,))
530 738
531 if len(text) < 2 or text[-1] != text[0]: 739 if len(text) < 2 or text[-1] != text[0]:
532 raise self._ParseError('String missing ending quote.') 740 raise self._ParseError('String missing ending quote: %r' % (text,))
533 741
534 try: 742 try:
535 result = _CUnescape(text[1:-1]) 743 result = text_encoding.CUnescape(text[1:-1])
536 except ValueError, e: 744 except ValueError, e:
537 raise self._ParseError(str(e)) 745 raise self._ParseError(str(e))
538 self.NextToken() 746 self.NextToken()
539 return result 747 return result
540 748
541 def ConsumeEnum(self, field): 749 def ConsumeEnum(self, field):
542 try: 750 try:
543 result = ParseEnum(field, self.token) 751 result = ParseEnum(field, self.token)
544 except ValueError, e: 752 except ValueError, e:
545 raise self._ParseError(str(e)) 753 raise self._ParseError(str(e))
(...skipping 21 matching lines...) Expand all
567 return self._ParseError('Couldn\'t parse string: ' + str(e)) 775 return self._ParseError('Couldn\'t parse string: ' + str(e))
568 776
569 def NextToken(self): 777 def NextToken(self):
570 """Reads the next meaningful token.""" 778 """Reads the next meaningful token."""
571 self._previous_line = self._line 779 self._previous_line = self._line
572 self._previous_column = self._column 780 self._previous_column = self._column
573 781
574 self._column += len(self.token) 782 self._column += len(self.token)
575 self._SkipWhitespace() 783 self._SkipWhitespace()
576 784
577 if not self._lines and len(self._current_line) <= self._column: 785 if not self._more_lines:
578 self.token = '' 786 self.token = ''
579 return 787 return
580 788
581 match = self._TOKEN.match(self._current_line, self._column) 789 match = self._TOKEN.match(self._current_line, self._column)
582 if match: 790 if match:
583 token = match.group(0) 791 token = match.group(0)
584 self.token = token 792 self.token = token
585 else: 793 else:
586 self.token = self._current_line[self._column] 794 self.token = self._current_line[self._column]
587 795
588 796
589 # text.encode('string_escape') does not seem to satisfy our needs as it
590 # encodes unprintable characters using two-digit hex escapes whereas our
591 # C++ unescaping function allows hex escapes to be any length. So,
592 # "\0011".encode('string_escape') ends up being "\\x011", which will be
593 # decoded in C++ as a single-character string with char code 0x11.
594 def _CEscape(text, as_utf8):
595 def escape(c):
596 o = ord(c)
597 if o == 10: return r"\n" # optional escape
598 if o == 13: return r"\r" # optional escape
599 if o == 9: return r"\t" # optional escape
600 if o == 39: return r"\'" # optional escape
601
602 if o == 34: return r'\"' # necessary escape
603 if o == 92: return r"\\" # necessary escape
604
605 # necessary escapes
606 if not as_utf8 and (o >= 127 or o < 32): return "\\%03o" % o
607 return c
608 return "".join([escape(c) for c in text])
609
610
611 _CUNESCAPE_HEX = re.compile('\\\\x([0-9a-fA-F]{2}|[0-9a-fA-F])')
612
613
614 def _CUnescape(text):
615 def ReplaceHex(m):
616 return chr(int(m.group(0)[2:], 16))
617 # This is required because the 'string_escape' encoding doesn't
618 # allow single-digit hex escapes (like '\xf').
619 result = _CUNESCAPE_HEX.sub(ReplaceHex, text)
620 return result.decode('string_escape')
621
622
623 def ParseInteger(text, is_signed=False, is_long=False): 797 def ParseInteger(text, is_signed=False, is_long=False):
624 """Parses an integer. 798 """Parses an integer.
625 799
626 Args: 800 Args:
627 text: The text to parse. 801 text: The text to parse.
628 is_signed: True if a signed integer must be parsed. 802 is_signed: True if a signed integer must be parsed.
629 is_long: True if a long integer must be parsed. 803 is_long: True if a long integer must be parsed.
630 804
631 Returns: 805 Returns:
632 The integer value. 806 The integer value.
633 807
634 Raises: 808 Raises:
635 ValueError: Thrown Iff the text is not a valid integer. 809 ValueError: Thrown Iff the text is not a valid integer.
636 """ 810 """
637 # Do the actual parsing. Exception handling is propagated to caller. 811 # Do the actual parsing. Exception handling is propagated to caller.
638 try: 812 try:
639 result = int(text, 0) 813 # We force 32-bit values to int and 64-bit values to long to make
814 # alternate implementations where the distinction is more significant
815 # (e.g. the C++ implementation) simpler.
816 if is_long:
817 result = long(text, 0)
818 else:
819 result = int(text, 0)
640 except ValueError: 820 except ValueError:
641 raise ValueError('Couldn\'t parse integer: %s' % text) 821 raise ValueError('Couldn\'t parse integer: %s' % text)
642 822
643 # Check if the integer is sane. Exceptions handled by callers. 823 # Check if the integer is sane. Exceptions handled by callers.
644 checker = _INTEGER_CHECKERS[2 * int(is_long) + int(is_signed)] 824 checker = _INTEGER_CHECKERS[2 * int(is_long) + int(is_signed)]
645 checker.CheckValue(result) 825 checker.CheckValue(result)
646 return result 826 return result
647 827
648 828
649 def ParseFloat(text): 829 def ParseFloat(text):
(...skipping 75 matching lines...) Expand 10 before | Expand all | Expand 10 after
725 'Enum type "%s" has no value named %s.' % ( 905 'Enum type "%s" has no value named %s.' % (
726 enum_descriptor.full_name, value)) 906 enum_descriptor.full_name, value))
727 else: 907 else:
728 # Numeric value. 908 # Numeric value.
729 enum_value = enum_descriptor.values_by_number.get(number, None) 909 enum_value = enum_descriptor.values_by_number.get(number, None)
730 if enum_value is None: 910 if enum_value is None:
731 raise ValueError( 911 raise ValueError(
732 'Enum type "%s" has no value with number %d.' % ( 912 'Enum type "%s" has no value with number %d.' % (
733 enum_descriptor.full_name, number)) 913 enum_descriptor.full_name, number))
734 return enum_value.number 914 return enum_value.number
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698