• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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