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 routines for printing protocol messages in text format. 32 33Simple usage example: 34 35 # Create a proto object and serialize it to a text proto string. 36 message = my_proto_pb2.MyMessage(foo='bar') 37 text_proto = text_format.MessageToString(message) 38 39 # Parse a text proto string. 40 message = text_format.Parse(text_proto, my_proto_pb2.MyMessage()) 41""" 42 43__author__ = 'kenton@google.com (Kenton Varda)' 44 45import io 46import re 47 48import six 49 50if six.PY3: 51 long = int # pylint: disable=redefined-builtin,invalid-name 52 53# pylint: disable=g-import-not-at-top 54from google.protobuf.internal import type_checkers 55from google.protobuf import descriptor 56from google.protobuf import text_encoding 57 58__all__ = ['MessageToString', 'PrintMessage', 'PrintField', 'PrintFieldValue', 59 'Merge'] 60 61_INTEGER_CHECKERS = (type_checkers.Uint32ValueChecker(), 62 type_checkers.Int32ValueChecker(), 63 type_checkers.Uint64ValueChecker(), 64 type_checkers.Int64ValueChecker()) 65_FLOAT_INFINITY = re.compile('-?inf(?:inity)?f?', re.IGNORECASE) 66_FLOAT_NAN = re.compile('nanf?', re.IGNORECASE) 67_FLOAT_TYPES = frozenset([descriptor.FieldDescriptor.CPPTYPE_FLOAT, 68 descriptor.FieldDescriptor.CPPTYPE_DOUBLE]) 69_QUOTES = frozenset(("'", '"')) 70_ANY_FULL_TYPE_NAME = 'google.protobuf.Any' 71 72 73class Error(Exception): 74 """Top-level module error for text_format.""" 75 76 77class ParseError(Error): 78 """Thrown in case of text parsing or tokenizing error.""" 79 80 def __init__(self, message=None, line=None, column=None): 81 if message is not None and line is not None: 82 loc = str(line) 83 if column is not None: 84 loc += ':{0}'.format(column) 85 message = '{0} : {1}'.format(loc, message) 86 if message is not None: 87 super(ParseError, self).__init__(message) 88 else: 89 super(ParseError, self).__init__() 90 self._line = line 91 self._column = column 92 93 def GetLine(self): 94 return self._line 95 96 def GetColumn(self): 97 return self._column 98 99 100class TextWriter(object): 101 102 def __init__(self, as_utf8): 103 if six.PY2: 104 self._writer = io.BytesIO() 105 else: 106 self._writer = io.StringIO() 107 108 def write(self, val): 109 if six.PY2: 110 if isinstance(val, six.text_type): 111 val = val.encode('utf-8') 112 return self._writer.write(val) 113 114 def close(self): 115 return self._writer.close() 116 117 def getvalue(self): 118 return self._writer.getvalue() 119 120 121def MessageToString(message, 122 as_utf8=False, 123 as_one_line=False, 124 pointy_brackets=False, 125 use_index_order=False, 126 float_format=None, 127 use_field_number=False, 128 descriptor_pool=None, 129 indent=0): 130 """Convert protobuf message to text format. 131 132 Floating point values can be formatted compactly with 15 digits of 133 precision (which is the most that IEEE 754 "double" can guarantee) 134 using float_format='.15g'. To ensure that converting to text and back to a 135 proto will result in an identical value, float_format='.17g' should be used. 136 137 Args: 138 message: The protocol buffers message. 139 as_utf8: Produce text output in UTF8 format. 140 as_one_line: Don't introduce newlines between fields. 141 pointy_brackets: If True, use angle brackets instead of curly braces for 142 nesting. 143 use_index_order: If True, print fields of a proto message using the order 144 defined in source code instead of the field number. By default, use the 145 field number order. 146 float_format: If set, use this to specify floating point number formatting 147 (per the "Format Specification Mini-Language"); otherwise, str() is used. 148 use_field_number: If True, print field numbers instead of names. 149 descriptor_pool: A DescriptorPool used to resolve Any types. 150 indent: The indent level, in terms of spaces, for pretty print. 151 152 Returns: 153 A string of the text formatted protocol buffer message. 154 """ 155 out = TextWriter(as_utf8) 156 printer = _Printer(out, indent, as_utf8, as_one_line, pointy_brackets, 157 use_index_order, float_format, use_field_number, 158 descriptor_pool) 159 printer.PrintMessage(message) 160 result = out.getvalue() 161 out.close() 162 if as_one_line: 163 return result.rstrip() 164 return result 165 166 167def _IsMapEntry(field): 168 return (field.type == descriptor.FieldDescriptor.TYPE_MESSAGE and 169 field.message_type.has_options and 170 field.message_type.GetOptions().map_entry) 171 172 173def PrintMessage(message, 174 out, 175 indent=0, 176 as_utf8=False, 177 as_one_line=False, 178 pointy_brackets=False, 179 use_index_order=False, 180 float_format=None, 181 use_field_number=False, 182 descriptor_pool=None): 183 printer = _Printer(out, indent, as_utf8, as_one_line, pointy_brackets, 184 use_index_order, float_format, use_field_number, 185 descriptor_pool) 186 printer.PrintMessage(message) 187 188 189def PrintField(field, 190 value, 191 out, 192 indent=0, 193 as_utf8=False, 194 as_one_line=False, 195 pointy_brackets=False, 196 use_index_order=False, 197 float_format=None): 198 """Print a single field name/value pair.""" 199 printer = _Printer(out, indent, as_utf8, as_one_line, pointy_brackets, 200 use_index_order, float_format) 201 printer.PrintField(field, value) 202 203 204def PrintFieldValue(field, 205 value, 206 out, 207 indent=0, 208 as_utf8=False, 209 as_one_line=False, 210 pointy_brackets=False, 211 use_index_order=False, 212 float_format=None): 213 """Print a single field value (not including name).""" 214 printer = _Printer(out, indent, as_utf8, as_one_line, pointy_brackets, 215 use_index_order, float_format) 216 printer.PrintFieldValue(field, value) 217 218 219def _BuildMessageFromTypeName(type_name, descriptor_pool): 220 """Returns a protobuf message instance. 221 222 Args: 223 type_name: Fully-qualified protobuf message type name string. 224 descriptor_pool: DescriptorPool instance. 225 226 Returns: 227 A Message instance of type matching type_name, or None if the a Descriptor 228 wasn't found matching type_name. 229 """ 230 # pylint: disable=g-import-not-at-top 231 from google.protobuf import message_factory 232 factory = message_factory.MessageFactory(descriptor_pool) 233 try: 234 message_descriptor = descriptor_pool.FindMessageTypeByName(type_name) 235 except KeyError: 236 return None 237 message_type = factory.GetPrototype(message_descriptor) 238 return message_type() 239 240 241class _Printer(object): 242 """Text format printer for protocol message.""" 243 244 def __init__(self, 245 out, 246 indent=0, 247 as_utf8=False, 248 as_one_line=False, 249 pointy_brackets=False, 250 use_index_order=False, 251 float_format=None, 252 use_field_number=False, 253 descriptor_pool=None): 254 """Initialize the Printer. 255 256 Floating point values can be formatted compactly with 15 digits of 257 precision (which is the most that IEEE 754 "double" can guarantee) 258 using float_format='.15g'. To ensure that converting to text and back to a 259 proto will result in an identical value, float_format='.17g' should be used. 260 261 Args: 262 out: To record the text format result. 263 indent: The indent level for pretty print. 264 as_utf8: Produce text output in UTF8 format. 265 as_one_line: Don't introduce newlines between fields. 266 pointy_brackets: If True, use angle brackets instead of curly braces for 267 nesting. 268 use_index_order: If True, print fields of a proto message using the order 269 defined in source code instead of the field number. By default, use the 270 field number order. 271 float_format: If set, use this to specify floating point number formatting 272 (per the "Format Specification Mini-Language"); otherwise, str() is 273 used. 274 use_field_number: If True, print field numbers instead of names. 275 descriptor_pool: A DescriptorPool used to resolve Any types. 276 """ 277 self.out = out 278 self.indent = indent 279 self.as_utf8 = as_utf8 280 self.as_one_line = as_one_line 281 self.pointy_brackets = pointy_brackets 282 self.use_index_order = use_index_order 283 self.float_format = float_format 284 self.use_field_number = use_field_number 285 self.descriptor_pool = descriptor_pool 286 287 def _TryPrintAsAnyMessage(self, message): 288 """Serializes if message is a google.protobuf.Any field.""" 289 packed_message = _BuildMessageFromTypeName(message.TypeName(), 290 self.descriptor_pool) 291 if packed_message: 292 packed_message.MergeFromString(message.value) 293 self.out.write('%s[%s]' % (self.indent * ' ', message.type_url)) 294 self._PrintMessageFieldValue(packed_message) 295 self.out.write(' ' if self.as_one_line else '\n') 296 return True 297 else: 298 return False 299 300 def PrintMessage(self, message): 301 """Convert protobuf message to text format. 302 303 Args: 304 message: The protocol buffers message. 305 """ 306 if (message.DESCRIPTOR.full_name == _ANY_FULL_TYPE_NAME and 307 self.descriptor_pool and self._TryPrintAsAnyMessage(message)): 308 return 309 fields = message.ListFields() 310 if self.use_index_order: 311 fields.sort(key=lambda x: x[0].index) 312 for field, value in fields: 313 if _IsMapEntry(field): 314 for key in sorted(value): 315 # This is slow for maps with submessage entires because it copies the 316 # entire tree. Unfortunately this would take significant refactoring 317 # of this file to work around. 318 # 319 # TODO(haberman): refactor and optimize if this becomes an issue. 320 entry_submsg = field.message_type._concrete_class(key=key, 321 value=value[key]) 322 self.PrintField(field, entry_submsg) 323 elif field.label == descriptor.FieldDescriptor.LABEL_REPEATED: 324 for element in value: 325 self.PrintField(field, element) 326 else: 327 self.PrintField(field, value) 328 329 def PrintField(self, field, value): 330 """Print a single field name/value pair.""" 331 out = self.out 332 out.write(' ' * self.indent) 333 if self.use_field_number: 334 out.write(str(field.number)) 335 else: 336 if field.is_extension: 337 out.write('[') 338 if (field.containing_type.GetOptions().message_set_wire_format and 339 field.type == descriptor.FieldDescriptor.TYPE_MESSAGE and 340 field.label == descriptor.FieldDescriptor.LABEL_OPTIONAL): 341 out.write(field.message_type.full_name) 342 else: 343 out.write(field.full_name) 344 out.write(']') 345 elif field.type == descriptor.FieldDescriptor.TYPE_GROUP: 346 # For groups, use the capitalized name. 347 out.write(field.message_type.name) 348 else: 349 out.write(field.name) 350 351 if field.cpp_type != descriptor.FieldDescriptor.CPPTYPE_MESSAGE: 352 # The colon is optional in this case, but our cross-language golden files 353 # don't include it. 354 out.write(': ') 355 356 self.PrintFieldValue(field, value) 357 if self.as_one_line: 358 out.write(' ') 359 else: 360 out.write('\n') 361 362 def _PrintMessageFieldValue(self, value): 363 if self.pointy_brackets: 364 openb = '<' 365 closeb = '>' 366 else: 367 openb = '{' 368 closeb = '}' 369 370 if self.as_one_line: 371 self.out.write(' %s ' % openb) 372 self.PrintMessage(value) 373 self.out.write(closeb) 374 else: 375 self.out.write(' %s\n' % openb) 376 self.indent += 2 377 self.PrintMessage(value) 378 self.indent -= 2 379 self.out.write(' ' * self.indent + closeb) 380 381 def PrintFieldValue(self, field, value): 382 """Print a single field value (not including name). 383 384 For repeated fields, the value should be a single element. 385 386 Args: 387 field: The descriptor of the field to be printed. 388 value: The value of the field. 389 """ 390 out = self.out 391 if field.cpp_type == descriptor.FieldDescriptor.CPPTYPE_MESSAGE: 392 self._PrintMessageFieldValue(value) 393 elif field.cpp_type == descriptor.FieldDescriptor.CPPTYPE_ENUM: 394 enum_value = field.enum_type.values_by_number.get(value, None) 395 if enum_value is not None: 396 out.write(enum_value.name) 397 else: 398 out.write(str(value)) 399 elif field.cpp_type == descriptor.FieldDescriptor.CPPTYPE_STRING: 400 out.write('\"') 401 if isinstance(value, six.text_type): 402 out_value = value.encode('utf-8') 403 else: 404 out_value = value 405 if field.type == descriptor.FieldDescriptor.TYPE_BYTES: 406 # We need to escape non-UTF8 chars in TYPE_BYTES field. 407 out_as_utf8 = False 408 else: 409 out_as_utf8 = self.as_utf8 410 out.write(text_encoding.CEscape(out_value, out_as_utf8)) 411 out.write('\"') 412 elif field.cpp_type == descriptor.FieldDescriptor.CPPTYPE_BOOL: 413 if value: 414 out.write('true') 415 else: 416 out.write('false') 417 elif field.cpp_type in _FLOAT_TYPES and self.float_format is not None: 418 out.write('{1:{0}}'.format(self.float_format, value)) 419 else: 420 out.write(str(value)) 421 422 423def Parse(text, 424 message, 425 allow_unknown_extension=False, 426 allow_field_number=False): 427 """Parses a text representation of a protocol message into a message. 428 429 Args: 430 text: Message text representation. 431 message: A protocol buffer message to merge into. 432 allow_unknown_extension: if True, skip over missing extensions and keep 433 parsing 434 allow_field_number: if True, both field number and field name are allowed. 435 436 Returns: 437 The same message passed as argument. 438 439 Raises: 440 ParseError: On text parsing problems. 441 """ 442 if not isinstance(text, str): 443 text = text.decode('utf-8') 444 return ParseLines( 445 text.split('\n'), message, allow_unknown_extension, allow_field_number) 446 447 448def Merge(text, 449 message, 450 allow_unknown_extension=False, 451 allow_field_number=False, 452 descriptor_pool=None): 453 """Parses a text representation of a protocol message into a message. 454 455 Like Parse(), but allows repeated values for a non-repeated field, and uses 456 the last one. 457 458 Args: 459 text: Message text representation. 460 message: A protocol buffer message to merge into. 461 allow_unknown_extension: if True, skip over missing extensions and keep 462 parsing 463 allow_field_number: if True, both field number and field name are allowed. 464 descriptor_pool: A DescriptorPool used to resolve Any types. 465 466 Returns: 467 The same message passed as argument. 468 469 Raises: 470 ParseError: On text parsing problems. 471 """ 472 return MergeLines( 473 text.split('\n'), 474 message, 475 allow_unknown_extension, 476 allow_field_number, 477 descriptor_pool=descriptor_pool) 478 479 480def ParseLines(lines, 481 message, 482 allow_unknown_extension=False, 483 allow_field_number=False): 484 """Parses a text representation of a protocol message into a message. 485 486 Args: 487 lines: An iterable of lines of a message's text representation. 488 message: A protocol buffer message to merge into. 489 allow_unknown_extension: if True, skip over missing extensions and keep 490 parsing 491 allow_field_number: if True, both field number and field name are allowed. 492 descriptor_pool: A DescriptorPool used to resolve Any types. 493 494 Returns: 495 The same message passed as argument. 496 497 Raises: 498 ParseError: On text parsing problems. 499 """ 500 parser = _Parser(allow_unknown_extension, allow_field_number) 501 return parser.ParseLines(lines, message) 502 503 504def MergeLines(lines, 505 message, 506 allow_unknown_extension=False, 507 allow_field_number=False, 508 descriptor_pool=None): 509 """Parses a text representation of a protocol message into a message. 510 511 Args: 512 lines: An iterable of lines of a message's text representation. 513 message: A protocol buffer message to merge into. 514 allow_unknown_extension: if True, skip over missing extensions and keep 515 parsing 516 allow_field_number: if True, both field number and field name are allowed. 517 518 Returns: 519 The same message passed as argument. 520 521 Raises: 522 ParseError: On text parsing problems. 523 """ 524 parser = _Parser(allow_unknown_extension, 525 allow_field_number, 526 descriptor_pool=descriptor_pool) 527 return parser.MergeLines(lines, message) 528 529 530class _Parser(object): 531 """Text format parser for protocol message.""" 532 533 def __init__(self, 534 allow_unknown_extension=False, 535 allow_field_number=False, 536 descriptor_pool=None): 537 self.allow_unknown_extension = allow_unknown_extension 538 self.allow_field_number = allow_field_number 539 self.descriptor_pool = descriptor_pool 540 541 def ParseFromString(self, text, message): 542 """Parses a text representation of a protocol message into a message.""" 543 if not isinstance(text, str): 544 text = text.decode('utf-8') 545 return self.ParseLines(text.split('\n'), message) 546 547 def ParseLines(self, lines, message): 548 """Parses a text representation of a protocol message into a message.""" 549 self._allow_multiple_scalars = False 550 self._ParseOrMerge(lines, message) 551 return message 552 553 def MergeFromString(self, text, message): 554 """Merges a text representation of a protocol message into a message.""" 555 return self._MergeLines(text.split('\n'), message) 556 557 def MergeLines(self, lines, message): 558 """Merges a text representation of a protocol message into a message.""" 559 self._allow_multiple_scalars = True 560 self._ParseOrMerge(lines, message) 561 return message 562 563 def _ParseOrMerge(self, lines, message): 564 """Converts a text representation of a protocol message into a message. 565 566 Args: 567 lines: Lines of a message's text representation. 568 message: A protocol buffer message to merge into. 569 570 Raises: 571 ParseError: On text parsing problems. 572 """ 573 tokenizer = Tokenizer(lines) 574 while not tokenizer.AtEnd(): 575 self._MergeField(tokenizer, message) 576 577 def _MergeField(self, tokenizer, message): 578 """Merges a single protocol message field into a message. 579 580 Args: 581 tokenizer: A tokenizer to parse the field name and values. 582 message: A protocol message to record the data. 583 584 Raises: 585 ParseError: In case of text parsing problems. 586 """ 587 message_descriptor = message.DESCRIPTOR 588 if (hasattr(message_descriptor, 'syntax') and 589 message_descriptor.syntax == 'proto3'): 590 # Proto3 doesn't represent presence so we can't test if multiple 591 # scalars have occurred. We have to allow them. 592 self._allow_multiple_scalars = True 593 if tokenizer.TryConsume('['): 594 name = [tokenizer.ConsumeIdentifier()] 595 while tokenizer.TryConsume('.'): 596 name.append(tokenizer.ConsumeIdentifier()) 597 name = '.'.join(name) 598 599 if not message_descriptor.is_extendable: 600 raise tokenizer.ParseErrorPreviousToken( 601 'Message type "%s" does not have extensions.' % 602 message_descriptor.full_name) 603 # pylint: disable=protected-access 604 field = message.Extensions._FindExtensionByName(name) 605 # pylint: enable=protected-access 606 if not field: 607 if self.allow_unknown_extension: 608 field = None 609 else: 610 raise tokenizer.ParseErrorPreviousToken( 611 'Extension "%s" not registered.' % name) 612 elif message_descriptor != field.containing_type: 613 raise tokenizer.ParseErrorPreviousToken( 614 'Extension "%s" does not extend message type "%s".' % 615 (name, message_descriptor.full_name)) 616 617 tokenizer.Consume(']') 618 619 else: 620 name = tokenizer.ConsumeIdentifierOrNumber() 621 if self.allow_field_number and name.isdigit(): 622 number = ParseInteger(name, True, True) 623 field = message_descriptor.fields_by_number.get(number, None) 624 if not field and message_descriptor.is_extendable: 625 field = message.Extensions._FindExtensionByNumber(number) 626 else: 627 field = message_descriptor.fields_by_name.get(name, None) 628 629 # Group names are expected to be capitalized as they appear in the 630 # .proto file, which actually matches their type names, not their field 631 # names. 632 if not field: 633 field = message_descriptor.fields_by_name.get(name.lower(), None) 634 if field and field.type != descriptor.FieldDescriptor.TYPE_GROUP: 635 field = None 636 637 if (field and field.type == descriptor.FieldDescriptor.TYPE_GROUP and 638 field.message_type.name != name): 639 field = None 640 641 if not field: 642 raise tokenizer.ParseErrorPreviousToken( 643 'Message type "%s" has no field named "%s".' % 644 (message_descriptor.full_name, name)) 645 646 if field: 647 if not self._allow_multiple_scalars and field.containing_oneof: 648 # Check if there's a different field set in this oneof. 649 # Note that we ignore the case if the same field was set before, and we 650 # apply _allow_multiple_scalars to non-scalar fields as well. 651 which_oneof = message.WhichOneof(field.containing_oneof.name) 652 if which_oneof is not None and which_oneof != field.name: 653 raise tokenizer.ParseErrorPreviousToken( 654 'Field "%s" is specified along with field "%s", another member ' 655 'of oneof "%s" for message type "%s".' % 656 (field.name, which_oneof, field.containing_oneof.name, 657 message_descriptor.full_name)) 658 659 if field.cpp_type == descriptor.FieldDescriptor.CPPTYPE_MESSAGE: 660 tokenizer.TryConsume(':') 661 merger = self._MergeMessageField 662 else: 663 tokenizer.Consume(':') 664 merger = self._MergeScalarField 665 666 if (field.label == descriptor.FieldDescriptor.LABEL_REPEATED and 667 tokenizer.TryConsume('[')): 668 # Short repeated format, e.g. "foo: [1, 2, 3]" 669 while True: 670 merger(tokenizer, message, field) 671 if tokenizer.TryConsume(']'): 672 break 673 tokenizer.Consume(',') 674 675 else: 676 merger(tokenizer, message, field) 677 678 else: # Proto field is unknown. 679 assert self.allow_unknown_extension 680 _SkipFieldContents(tokenizer) 681 682 # For historical reasons, fields may optionally be separated by commas or 683 # semicolons. 684 if not tokenizer.TryConsume(','): 685 tokenizer.TryConsume(';') 686 687 def _ConsumeAnyTypeUrl(self, tokenizer): 688 """Consumes a google.protobuf.Any type URL and returns the type name.""" 689 # Consume "type.googleapis.com/". 690 tokenizer.ConsumeIdentifier() 691 tokenizer.Consume('.') 692 tokenizer.ConsumeIdentifier() 693 tokenizer.Consume('.') 694 tokenizer.ConsumeIdentifier() 695 tokenizer.Consume('/') 696 # Consume the fully-qualified type name. 697 name = [tokenizer.ConsumeIdentifier()] 698 while tokenizer.TryConsume('.'): 699 name.append(tokenizer.ConsumeIdentifier()) 700 return '.'.join(name) 701 702 def _MergeMessageField(self, tokenizer, message, field): 703 """Merges a single scalar field into a message. 704 705 Args: 706 tokenizer: A tokenizer to parse the field value. 707 message: The message of which field is a member. 708 field: The descriptor of the field to be merged. 709 710 Raises: 711 ParseError: In case of text parsing problems. 712 """ 713 is_map_entry = _IsMapEntry(field) 714 715 if tokenizer.TryConsume('<'): 716 end_token = '>' 717 else: 718 tokenizer.Consume('{') 719 end_token = '}' 720 721 if (field.message_type.full_name == _ANY_FULL_TYPE_NAME and 722 tokenizer.TryConsume('[')): 723 packed_type_name = self._ConsumeAnyTypeUrl(tokenizer) 724 tokenizer.Consume(']') 725 tokenizer.TryConsume(':') 726 if tokenizer.TryConsume('<'): 727 expanded_any_end_token = '>' 728 else: 729 tokenizer.Consume('{') 730 expanded_any_end_token = '}' 731 if not self.descriptor_pool: 732 raise ParseError('Descriptor pool required to parse expanded Any field') 733 expanded_any_sub_message = _BuildMessageFromTypeName(packed_type_name, 734 self.descriptor_pool) 735 if not expanded_any_sub_message: 736 raise ParseError('Type %s not found in descriptor pool' % 737 packed_type_name) 738 while not tokenizer.TryConsume(expanded_any_end_token): 739 if tokenizer.AtEnd(): 740 raise tokenizer.ParseErrorPreviousToken('Expected "%s".' % 741 (expanded_any_end_token,)) 742 self._MergeField(tokenizer, expanded_any_sub_message) 743 if field.label == descriptor.FieldDescriptor.LABEL_REPEATED: 744 any_message = getattr(message, field.name).add() 745 else: 746 any_message = getattr(message, field.name) 747 any_message.Pack(expanded_any_sub_message) 748 elif field.label == descriptor.FieldDescriptor.LABEL_REPEATED: 749 if field.is_extension: 750 sub_message = message.Extensions[field].add() 751 elif is_map_entry: 752 # pylint: disable=protected-access 753 sub_message = field.message_type._concrete_class() 754 else: 755 sub_message = getattr(message, field.name).add() 756 else: 757 if field.is_extension: 758 sub_message = message.Extensions[field] 759 else: 760 sub_message = getattr(message, field.name) 761 sub_message.SetInParent() 762 763 while not tokenizer.TryConsume(end_token): 764 if tokenizer.AtEnd(): 765 raise tokenizer.ParseErrorPreviousToken('Expected "%s".' % (end_token,)) 766 self._MergeField(tokenizer, sub_message) 767 768 if is_map_entry: 769 value_cpptype = field.message_type.fields_by_name['value'].cpp_type 770 if value_cpptype == descriptor.FieldDescriptor.CPPTYPE_MESSAGE: 771 value = getattr(message, field.name)[sub_message.key] 772 value.MergeFrom(sub_message.value) 773 else: 774 getattr(message, field.name)[sub_message.key] = sub_message.value 775 776 def _MergeScalarField(self, tokenizer, message, field): 777 """Merges a single scalar field into a message. 778 779 Args: 780 tokenizer: A tokenizer to parse the field value. 781 message: A protocol message to record the data. 782 field: The descriptor of the field to be merged. 783 784 Raises: 785 ParseError: In case of text parsing problems. 786 RuntimeError: On runtime errors. 787 """ 788 _ = self.allow_unknown_extension 789 value = None 790 791 if field.type in (descriptor.FieldDescriptor.TYPE_INT32, 792 descriptor.FieldDescriptor.TYPE_SINT32, 793 descriptor.FieldDescriptor.TYPE_SFIXED32): 794 value = _ConsumeInt32(tokenizer) 795 elif field.type in (descriptor.FieldDescriptor.TYPE_INT64, 796 descriptor.FieldDescriptor.TYPE_SINT64, 797 descriptor.FieldDescriptor.TYPE_SFIXED64): 798 value = _ConsumeInt64(tokenizer) 799 elif field.type in (descriptor.FieldDescriptor.TYPE_UINT32, 800 descriptor.FieldDescriptor.TYPE_FIXED32): 801 value = _ConsumeUint32(tokenizer) 802 elif field.type in (descriptor.FieldDescriptor.TYPE_UINT64, 803 descriptor.FieldDescriptor.TYPE_FIXED64): 804 value = _ConsumeUint64(tokenizer) 805 elif field.type in (descriptor.FieldDescriptor.TYPE_FLOAT, 806 descriptor.FieldDescriptor.TYPE_DOUBLE): 807 value = tokenizer.ConsumeFloat() 808 elif field.type == descriptor.FieldDescriptor.TYPE_BOOL: 809 value = tokenizer.ConsumeBool() 810 elif field.type == descriptor.FieldDescriptor.TYPE_STRING: 811 value = tokenizer.ConsumeString() 812 elif field.type == descriptor.FieldDescriptor.TYPE_BYTES: 813 value = tokenizer.ConsumeByteString() 814 elif field.type == descriptor.FieldDescriptor.TYPE_ENUM: 815 value = tokenizer.ConsumeEnum(field) 816 else: 817 raise RuntimeError('Unknown field type %d' % field.type) 818 819 if field.label == descriptor.FieldDescriptor.LABEL_REPEATED: 820 if field.is_extension: 821 message.Extensions[field].append(value) 822 else: 823 getattr(message, field.name).append(value) 824 else: 825 if field.is_extension: 826 if not self._allow_multiple_scalars and message.HasExtension(field): 827 raise tokenizer.ParseErrorPreviousToken( 828 'Message type "%s" should not have multiple "%s" extensions.' % 829 (message.DESCRIPTOR.full_name, field.full_name)) 830 else: 831 message.Extensions[field] = value 832 else: 833 if not self._allow_multiple_scalars and message.HasField(field.name): 834 raise tokenizer.ParseErrorPreviousToken( 835 'Message type "%s" should not have multiple "%s" fields.' % 836 (message.DESCRIPTOR.full_name, field.name)) 837 else: 838 setattr(message, field.name, value) 839 840 841def _SkipFieldContents(tokenizer): 842 """Skips over contents (value or message) of a field. 843 844 Args: 845 tokenizer: A tokenizer to parse the field name and values. 846 """ 847 # Try to guess the type of this field. 848 # If this field is not a message, there should be a ":" between the 849 # field name and the field value and also the field value should not 850 # start with "{" or "<" which indicates the beginning of a message body. 851 # If there is no ":" or there is a "{" or "<" after ":", this field has 852 # to be a message or the input is ill-formed. 853 if tokenizer.TryConsume(':') and not tokenizer.LookingAt( 854 '{') and not tokenizer.LookingAt('<'): 855 _SkipFieldValue(tokenizer) 856 else: 857 _SkipFieldMessage(tokenizer) 858 859 860def _SkipField(tokenizer): 861 """Skips over a complete field (name and value/message). 862 863 Args: 864 tokenizer: A tokenizer to parse the field name and values. 865 """ 866 if tokenizer.TryConsume('['): 867 # Consume extension name. 868 tokenizer.ConsumeIdentifier() 869 while tokenizer.TryConsume('.'): 870 tokenizer.ConsumeIdentifier() 871 tokenizer.Consume(']') 872 else: 873 tokenizer.ConsumeIdentifier() 874 875 _SkipFieldContents(tokenizer) 876 877 # For historical reasons, fields may optionally be separated by commas or 878 # semicolons. 879 if not tokenizer.TryConsume(','): 880 tokenizer.TryConsume(';') 881 882 883def _SkipFieldMessage(tokenizer): 884 """Skips over a field message. 885 886 Args: 887 tokenizer: A tokenizer to parse the field name and values. 888 """ 889 890 if tokenizer.TryConsume('<'): 891 delimiter = '>' 892 else: 893 tokenizer.Consume('{') 894 delimiter = '}' 895 896 while not tokenizer.LookingAt('>') and not tokenizer.LookingAt('}'): 897 _SkipField(tokenizer) 898 899 tokenizer.Consume(delimiter) 900 901 902def _SkipFieldValue(tokenizer): 903 """Skips over a field value. 904 905 Args: 906 tokenizer: A tokenizer to parse the field name and values. 907 908 Raises: 909 ParseError: In case an invalid field value is found. 910 """ 911 # String/bytes tokens can come in multiple adjacent string literals. 912 # If we can consume one, consume as many as we can. 913 if tokenizer.TryConsumeByteString(): 914 while tokenizer.TryConsumeByteString(): 915 pass 916 return 917 918 if (not tokenizer.TryConsumeIdentifier() and 919 not _TryConsumeInt64(tokenizer) and not _TryConsumeUint64(tokenizer) and 920 not tokenizer.TryConsumeFloat()): 921 raise ParseError('Invalid field value: ' + tokenizer.token) 922 923 924class Tokenizer(object): 925 """Protocol buffer text representation tokenizer. 926 927 This class handles the lower level string parsing by splitting it into 928 meaningful tokens. 929 930 It was directly ported from the Java protocol buffer API. 931 """ 932 933 _WHITESPACE = re.compile(r'\s+') 934 _COMMENT = re.compile(r'(\s*#.*$)', re.MULTILINE) 935 _WHITESPACE_OR_COMMENT = re.compile(r'(\s|(#.*$))+', re.MULTILINE) 936 _TOKEN = re.compile('|'.join([ 937 r'[a-zA-Z_][0-9a-zA-Z_+-]*', # an identifier 938 r'([0-9+-]|(\.[0-9]))[0-9a-zA-Z_.+-]*', # a number 939 ] + [ # quoted str for each quote mark 940 r'{qt}([^{qt}\n\\]|\\.)*({qt}|\\?$)'.format(qt=mark) for mark in _QUOTES 941 ])) 942 943 _IDENTIFIER = re.compile(r'[^\d\W]\w*') 944 _IDENTIFIER_OR_NUMBER = re.compile(r'\w+') 945 946 def __init__(self, lines, skip_comments=True): 947 self._position = 0 948 self._line = -1 949 self._column = 0 950 self._token_start = None 951 self.token = '' 952 self._lines = iter(lines) 953 self._current_line = '' 954 self._previous_line = 0 955 self._previous_column = 0 956 self._more_lines = True 957 self._skip_comments = skip_comments 958 self._whitespace_pattern = (skip_comments and self._WHITESPACE_OR_COMMENT 959 or self._WHITESPACE) 960 self._SkipWhitespace() 961 self.NextToken() 962 963 def LookingAt(self, token): 964 return self.token == token 965 966 def AtEnd(self): 967 """Checks the end of the text was reached. 968 969 Returns: 970 True iff the end was reached. 971 """ 972 return not self.token 973 974 def _PopLine(self): 975 while len(self._current_line) <= self._column: 976 try: 977 self._current_line = next(self._lines) 978 except StopIteration: 979 self._current_line = '' 980 self._more_lines = False 981 return 982 else: 983 self._line += 1 984 self._column = 0 985 986 def _SkipWhitespace(self): 987 while True: 988 self._PopLine() 989 match = self._whitespace_pattern.match(self._current_line, self._column) 990 if not match: 991 break 992 length = len(match.group(0)) 993 self._column += length 994 995 def TryConsume(self, token): 996 """Tries to consume a given piece of text. 997 998 Args: 999 token: Text to consume. 1000 1001 Returns: 1002 True iff the text was consumed. 1003 """ 1004 if self.token == token: 1005 self.NextToken() 1006 return True 1007 return False 1008 1009 def Consume(self, token): 1010 """Consumes a piece of text. 1011 1012 Args: 1013 token: Text to consume. 1014 1015 Raises: 1016 ParseError: If the text couldn't be consumed. 1017 """ 1018 if not self.TryConsume(token): 1019 raise self.ParseError('Expected "%s".' % token) 1020 1021 def ConsumeComment(self): 1022 result = self.token 1023 if not self._COMMENT.match(result): 1024 raise self.ParseError('Expected comment.') 1025 self.NextToken() 1026 return result 1027 1028 def TryConsumeIdentifier(self): 1029 try: 1030 self.ConsumeIdentifier() 1031 return True 1032 except ParseError: 1033 return False 1034 1035 def ConsumeIdentifier(self): 1036 """Consumes protocol message field identifier. 1037 1038 Returns: 1039 Identifier string. 1040 1041 Raises: 1042 ParseError: If an identifier couldn't be consumed. 1043 """ 1044 result = self.token 1045 if not self._IDENTIFIER.match(result): 1046 raise self.ParseError('Expected identifier.') 1047 self.NextToken() 1048 return result 1049 1050 def TryConsumeIdentifierOrNumber(self): 1051 try: 1052 self.ConsumeIdentifierOrNumber() 1053 return True 1054 except ParseError: 1055 return False 1056 1057 def ConsumeIdentifierOrNumber(self): 1058 """Consumes protocol message field identifier. 1059 1060 Returns: 1061 Identifier string. 1062 1063 Raises: 1064 ParseError: If an identifier couldn't be consumed. 1065 """ 1066 result = self.token 1067 if not self._IDENTIFIER_OR_NUMBER.match(result): 1068 raise self.ParseError('Expected identifier or number.') 1069 self.NextToken() 1070 return result 1071 1072 def TryConsumeInteger(self): 1073 try: 1074 # Note: is_long only affects value type, not whether an error is raised. 1075 self.ConsumeInteger() 1076 return True 1077 except ParseError: 1078 return False 1079 1080 def ConsumeInteger(self, is_long=False): 1081 """Consumes an integer number. 1082 1083 Args: 1084 is_long: True if the value should be returned as a long integer. 1085 Returns: 1086 The integer parsed. 1087 1088 Raises: 1089 ParseError: If an integer couldn't be consumed. 1090 """ 1091 try: 1092 result = _ParseAbstractInteger(self.token, is_long=is_long) 1093 except ValueError as e: 1094 raise self.ParseError(str(e)) 1095 self.NextToken() 1096 return result 1097 1098 def TryConsumeFloat(self): 1099 try: 1100 self.ConsumeFloat() 1101 return True 1102 except ParseError: 1103 return False 1104 1105 def ConsumeFloat(self): 1106 """Consumes an floating point number. 1107 1108 Returns: 1109 The number parsed. 1110 1111 Raises: 1112 ParseError: If a floating point number couldn't be consumed. 1113 """ 1114 try: 1115 result = ParseFloat(self.token) 1116 except ValueError as e: 1117 raise self.ParseError(str(e)) 1118 self.NextToken() 1119 return result 1120 1121 def ConsumeBool(self): 1122 """Consumes a boolean value. 1123 1124 Returns: 1125 The bool parsed. 1126 1127 Raises: 1128 ParseError: If a boolean value couldn't be consumed. 1129 """ 1130 try: 1131 result = ParseBool(self.token) 1132 except ValueError as e: 1133 raise self.ParseError(str(e)) 1134 self.NextToken() 1135 return result 1136 1137 def TryConsumeByteString(self): 1138 try: 1139 self.ConsumeByteString() 1140 return True 1141 except ParseError: 1142 return False 1143 1144 def ConsumeString(self): 1145 """Consumes a string value. 1146 1147 Returns: 1148 The string parsed. 1149 1150 Raises: 1151 ParseError: If a string value couldn't be consumed. 1152 """ 1153 the_bytes = self.ConsumeByteString() 1154 try: 1155 return six.text_type(the_bytes, 'utf-8') 1156 except UnicodeDecodeError as e: 1157 raise self._StringParseError(e) 1158 1159 def ConsumeByteString(self): 1160 """Consumes a byte array value. 1161 1162 Returns: 1163 The array parsed (as a string). 1164 1165 Raises: 1166 ParseError: If a byte array value couldn't be consumed. 1167 """ 1168 the_list = [self._ConsumeSingleByteString()] 1169 while self.token and self.token[0] in _QUOTES: 1170 the_list.append(self._ConsumeSingleByteString()) 1171 return b''.join(the_list) 1172 1173 def _ConsumeSingleByteString(self): 1174 """Consume one token of a string literal. 1175 1176 String literals (whether bytes or text) can come in multiple adjacent 1177 tokens which are automatically concatenated, like in C or Python. This 1178 method only consumes one token. 1179 1180 Returns: 1181 The token parsed. 1182 Raises: 1183 ParseError: When the wrong format data is found. 1184 """ 1185 text = self.token 1186 if len(text) < 1 or text[0] not in _QUOTES: 1187 raise self.ParseError('Expected string but found: %r' % (text,)) 1188 1189 if len(text) < 2 or text[-1] != text[0]: 1190 raise self.ParseError('String missing ending quote: %r' % (text,)) 1191 1192 try: 1193 result = text_encoding.CUnescape(text[1:-1]) 1194 except ValueError as e: 1195 raise self.ParseError(str(e)) 1196 self.NextToken() 1197 return result 1198 1199 def ConsumeEnum(self, field): 1200 try: 1201 result = ParseEnum(field, self.token) 1202 except ValueError as e: 1203 raise self.ParseError(str(e)) 1204 self.NextToken() 1205 return result 1206 1207 def ParseErrorPreviousToken(self, message): 1208 """Creates and *returns* a ParseError for the previously read token. 1209 1210 Args: 1211 message: A message to set for the exception. 1212 1213 Returns: 1214 A ParseError instance. 1215 """ 1216 return ParseError(message, self._previous_line + 1, 1217 self._previous_column + 1) 1218 1219 def ParseError(self, message): 1220 """Creates and *returns* a ParseError for the current token.""" 1221 return ParseError(message, self._line + 1, self._column + 1) 1222 1223 def _StringParseError(self, e): 1224 return self.ParseError('Couldn\'t parse string: ' + str(e)) 1225 1226 def NextToken(self): 1227 """Reads the next meaningful token.""" 1228 self._previous_line = self._line 1229 self._previous_column = self._column 1230 1231 self._column += len(self.token) 1232 self._SkipWhitespace() 1233 1234 if not self._more_lines: 1235 self.token = '' 1236 return 1237 1238 match = self._TOKEN.match(self._current_line, self._column) 1239 if not match and not self._skip_comments: 1240 match = self._COMMENT.match(self._current_line, self._column) 1241 if match: 1242 token = match.group(0) 1243 self.token = token 1244 else: 1245 self.token = self._current_line[self._column] 1246 1247# Aliased so it can still be accessed by current visibility violators. 1248# TODO(dbarnett): Migrate violators to textformat_tokenizer. 1249_Tokenizer = Tokenizer # pylint: disable=invalid-name 1250 1251 1252def _ConsumeInt32(tokenizer): 1253 """Consumes a signed 32bit integer number from tokenizer. 1254 1255 Args: 1256 tokenizer: A tokenizer used to parse the number. 1257 1258 Returns: 1259 The integer parsed. 1260 1261 Raises: 1262 ParseError: If a signed 32bit integer couldn't be consumed. 1263 """ 1264 return _ConsumeInteger(tokenizer, is_signed=True, is_long=False) 1265 1266 1267def _ConsumeUint32(tokenizer): 1268 """Consumes an unsigned 32bit integer number from tokenizer. 1269 1270 Args: 1271 tokenizer: A tokenizer used to parse the number. 1272 1273 Returns: 1274 The integer parsed. 1275 1276 Raises: 1277 ParseError: If an unsigned 32bit integer couldn't be consumed. 1278 """ 1279 return _ConsumeInteger(tokenizer, is_signed=False, is_long=False) 1280 1281 1282def _TryConsumeInt64(tokenizer): 1283 try: 1284 _ConsumeInt64(tokenizer) 1285 return True 1286 except ParseError: 1287 return False 1288 1289 1290def _ConsumeInt64(tokenizer): 1291 """Consumes a signed 32bit integer number from tokenizer. 1292 1293 Args: 1294 tokenizer: A tokenizer used to parse the number. 1295 1296 Returns: 1297 The integer parsed. 1298 1299 Raises: 1300 ParseError: If a signed 32bit integer couldn't be consumed. 1301 """ 1302 return _ConsumeInteger(tokenizer, is_signed=True, is_long=True) 1303 1304 1305def _TryConsumeUint64(tokenizer): 1306 try: 1307 _ConsumeUint64(tokenizer) 1308 return True 1309 except ParseError: 1310 return False 1311 1312 1313def _ConsumeUint64(tokenizer): 1314 """Consumes an unsigned 64bit integer number from tokenizer. 1315 1316 Args: 1317 tokenizer: A tokenizer used to parse the number. 1318 1319 Returns: 1320 The integer parsed. 1321 1322 Raises: 1323 ParseError: If an unsigned 64bit integer couldn't be consumed. 1324 """ 1325 return _ConsumeInteger(tokenizer, is_signed=False, is_long=True) 1326 1327 1328def _TryConsumeInteger(tokenizer, is_signed=False, is_long=False): 1329 try: 1330 _ConsumeInteger(tokenizer, is_signed=is_signed, is_long=is_long) 1331 return True 1332 except ParseError: 1333 return False 1334 1335 1336def _ConsumeInteger(tokenizer, is_signed=False, is_long=False): 1337 """Consumes an integer number from tokenizer. 1338 1339 Args: 1340 tokenizer: A tokenizer used to parse the number. 1341 is_signed: True if a signed integer must be parsed. 1342 is_long: True if a long integer must be parsed. 1343 1344 Returns: 1345 The integer parsed. 1346 1347 Raises: 1348 ParseError: If an integer with given characteristics couldn't be consumed. 1349 """ 1350 try: 1351 result = ParseInteger(tokenizer.token, is_signed=is_signed, is_long=is_long) 1352 except ValueError as e: 1353 raise tokenizer.ParseError(str(e)) 1354 tokenizer.NextToken() 1355 return result 1356 1357 1358def ParseInteger(text, is_signed=False, is_long=False): 1359 """Parses an integer. 1360 1361 Args: 1362 text: The text to parse. 1363 is_signed: True if a signed integer must be parsed. 1364 is_long: True if a long integer must be parsed. 1365 1366 Returns: 1367 The integer value. 1368 1369 Raises: 1370 ValueError: Thrown Iff the text is not a valid integer. 1371 """ 1372 # Do the actual parsing. Exception handling is propagated to caller. 1373 result = _ParseAbstractInteger(text, is_long=is_long) 1374 1375 # Check if the integer is sane. Exceptions handled by callers. 1376 checker = _INTEGER_CHECKERS[2 * int(is_long) + int(is_signed)] 1377 checker.CheckValue(result) 1378 return result 1379 1380 1381def _ParseAbstractInteger(text, is_long=False): 1382 """Parses an integer without checking size/signedness. 1383 1384 Args: 1385 text: The text to parse. 1386 is_long: True if the value should be returned as a long integer. 1387 1388 Returns: 1389 The integer value. 1390 1391 Raises: 1392 ValueError: Thrown Iff the text is not a valid integer. 1393 """ 1394 # Do the actual parsing. Exception handling is propagated to caller. 1395 try: 1396 # We force 32-bit values to int and 64-bit values to long to make 1397 # alternate implementations where the distinction is more significant 1398 # (e.g. the C++ implementation) simpler. 1399 if is_long: 1400 return long(text, 0) 1401 else: 1402 return int(text, 0) 1403 except ValueError: 1404 raise ValueError('Couldn\'t parse integer: %s' % text) 1405 1406 1407def ParseFloat(text): 1408 """Parse a floating point number. 1409 1410 Args: 1411 text: Text to parse. 1412 1413 Returns: 1414 The number parsed. 1415 1416 Raises: 1417 ValueError: If a floating point number couldn't be parsed. 1418 """ 1419 try: 1420 # Assume Python compatible syntax. 1421 return float(text) 1422 except ValueError: 1423 # Check alternative spellings. 1424 if _FLOAT_INFINITY.match(text): 1425 if text[0] == '-': 1426 return float('-inf') 1427 else: 1428 return float('inf') 1429 elif _FLOAT_NAN.match(text): 1430 return float('nan') 1431 else: 1432 # assume '1.0f' format 1433 try: 1434 return float(text.rstrip('f')) 1435 except ValueError: 1436 raise ValueError('Couldn\'t parse float: %s' % text) 1437 1438 1439def ParseBool(text): 1440 """Parse a boolean value. 1441 1442 Args: 1443 text: Text to parse. 1444 1445 Returns: 1446 Boolean values parsed 1447 1448 Raises: 1449 ValueError: If text is not a valid boolean. 1450 """ 1451 if text in ('true', 't', '1'): 1452 return True 1453 elif text in ('false', 'f', '0'): 1454 return False 1455 else: 1456 raise ValueError('Expected "true" or "false".') 1457 1458 1459def ParseEnum(field, value): 1460 """Parse an enum value. 1461 1462 The value can be specified by a number (the enum value), or by 1463 a string literal (the enum name). 1464 1465 Args: 1466 field: Enum field descriptor. 1467 value: String value. 1468 1469 Returns: 1470 Enum value number. 1471 1472 Raises: 1473 ValueError: If the enum value could not be parsed. 1474 """ 1475 enum_descriptor = field.enum_type 1476 try: 1477 number = int(value, 0) 1478 except ValueError: 1479 # Identifier. 1480 enum_value = enum_descriptor.values_by_name.get(value, None) 1481 if enum_value is None: 1482 raise ValueError('Enum type "%s" has no value named %s.' % 1483 (enum_descriptor.full_name, value)) 1484 else: 1485 # Numeric value. 1486 enum_value = enum_descriptor.values_by_number.get(number, None) 1487 if enum_value is None: 1488 raise ValueError('Enum type "%s" has no value with number %d.' % 1489 (enum_descriptor.full_name, number)) 1490 return enum_value.number 1491