Index: third_party/protobuf/python/google/protobuf/text_format.py |
=================================================================== |
--- third_party/protobuf/python/google/protobuf/text_format.py (revision 216642) |
+++ third_party/protobuf/python/google/protobuf/text_format.py (working copy) |
@@ -43,10 +43,12 @@ |
'PrintFieldValue', 'Merge' ] |
-# Infinity and NaN are not explicitly supported by Python pre-2.6, and |
-# float('inf') does not work on Windows (pre-2.6). |
-_INFINITY = 1e10000 # overflows, thus will actually be infinity. |
-_NAN = _INFINITY * 0 |
+_INTEGER_CHECKERS = (type_checkers.Uint32ValueChecker(), |
+ type_checkers.Int32ValueChecker(), |
+ type_checkers.Uint64ValueChecker(), |
+ type_checkers.Int64ValueChecker()) |
+_FLOAT_INFINITY = re.compile('-?inf(?:inity)?f?', re.IGNORECASE) |
+_FLOAT_NAN = re.compile('nanf?', re.IGNORECASE) |
class ParseError(Exception): |
@@ -120,7 +122,11 @@ |
PrintMessage(value, out, indent + 2, as_utf8, as_one_line) |
out.write(' ' * indent + '}') |
elif field.cpp_type == descriptor.FieldDescriptor.CPPTYPE_ENUM: |
- out.write(field.enum_type.values_by_number[value].name) |
+ enum_value = field.enum_type.values_by_number.get(value, None) |
+ if enum_value is not None: |
+ out.write(enum_value.name) |
+ else: |
+ out.write(str(value)) |
elif field.cpp_type == descriptor.FieldDescriptor.CPPTYPE_STRING: |
out.write('\"') |
if type(value) is unicode: |
@@ -271,24 +277,7 @@ |
elif field.type == descriptor.FieldDescriptor.TYPE_BYTES: |
value = tokenizer.ConsumeByteString() |
elif field.type == descriptor.FieldDescriptor.TYPE_ENUM: |
- # Enum can be specified by a number (the enum value), or by |
- # a string literal (the enum name). |
- enum_descriptor = field.enum_type |
- if tokenizer.LookingAtInteger(): |
- number = tokenizer.ConsumeInt32() |
- enum_value = enum_descriptor.values_by_number.get(number, None) |
- if enum_value is None: |
- raise tokenizer.ParseErrorPreviousToken( |
- 'Enum type "%s" has no value with number %d.' % ( |
- enum_descriptor.full_name, number)) |
- else: |
- identifier = tokenizer.ConsumeIdentifier() |
- enum_value = enum_descriptor.values_by_name.get(identifier, None) |
- if enum_value is None: |
- raise tokenizer.ParseErrorPreviousToken( |
- 'Enum type "%s" has no value named %s.' % ( |
- enum_descriptor.full_name, identifier)) |
- value = enum_value.number |
+ value = tokenizer.ConsumeEnum(field) |
else: |
raise RuntimeError('Unknown field type %d' % field.type) |
@@ -320,12 +309,6 @@ |
'\"([^\"\n\\\\]|\\\\.)*(\"|\\\\?$)|' # a double-quoted string |
'\'([^\'\n\\\\]|\\\\.)*(\'|\\\\?$)') # a single-quoted string |
_IDENTIFIER = re.compile('\w+') |
- _INTEGER_CHECKERS = [type_checkers.Uint32ValueChecker(), |
- type_checkers.Int32ValueChecker(), |
- type_checkers.Uint64ValueChecker(), |
- type_checkers.Int64ValueChecker()] |
- _FLOAT_INFINITY = re.compile('-?inf(inity)?f?', re.IGNORECASE) |
- _FLOAT_NAN = re.compile("nanf?", re.IGNORECASE) |
def __init__(self, text_message): |
self._text_message = text_message |
@@ -394,17 +377,6 @@ |
if not self.TryConsume(token): |
raise self._ParseError('Expected "%s".' % token) |
- def LookingAtInteger(self): |
- """Checks if the current token is an integer. |
- |
- Returns: |
- True iff the current token is an integer. |
- """ |
- if not self.token: |
- return False |
- c = self.token[0] |
- return (c >= '0' and c <= '9') or c == '-' or c == '+' |
- |
def ConsumeIdentifier(self): |
"""Consumes protocol message field identifier. |
@@ -430,9 +402,9 @@ |
ParseError: If a signed 32bit integer couldn't be consumed. |
""" |
try: |
- result = self._ParseInteger(self.token, is_signed=True, is_long=False) |
+ result = ParseInteger(self.token, is_signed=True, is_long=False) |
except ValueError, e: |
- raise self._IntegerParseError(e) |
+ raise self._ParseError(str(e)) |
self.NextToken() |
return result |
@@ -446,9 +418,9 @@ |
ParseError: If an unsigned 32bit integer couldn't be consumed. |
""" |
try: |
- result = self._ParseInteger(self.token, is_signed=False, is_long=False) |
+ result = ParseInteger(self.token, is_signed=False, is_long=False) |
except ValueError, e: |
- raise self._IntegerParseError(e) |
+ raise self._ParseError(str(e)) |
self.NextToken() |
return result |
@@ -462,9 +434,9 @@ |
ParseError: If a signed 64bit integer couldn't be consumed. |
""" |
try: |
- result = self._ParseInteger(self.token, is_signed=True, is_long=True) |
+ result = ParseInteger(self.token, is_signed=True, is_long=True) |
except ValueError, e: |
- raise self._IntegerParseError(e) |
+ raise self._ParseError(str(e)) |
self.NextToken() |
return result |
@@ -478,9 +450,9 @@ |
ParseError: If an unsigned 64bit integer couldn't be consumed. |
""" |
try: |
- result = self._ParseInteger(self.token, is_signed=False, is_long=True) |
+ result = ParseInteger(self.token, is_signed=False, is_long=True) |
except ValueError, e: |
- raise self._IntegerParseError(e) |
+ raise self._ParseError(str(e)) |
self.NextToken() |
return result |
@@ -493,21 +465,10 @@ |
Raises: |
ParseError: If a floating point number couldn't be consumed. |
""" |
- text = self.token |
- if self._FLOAT_INFINITY.match(text): |
- self.NextToken() |
- if text.startswith('-'): |
- return -_INFINITY |
- return _INFINITY |
- |
- if self._FLOAT_NAN.match(text): |
- self.NextToken() |
- return _NAN |
- |
try: |
- result = float(text) |
+ result = ParseFloat(self.token) |
except ValueError, e: |
- raise self._FloatParseError(e) |
+ raise self._ParseError(str(e)) |
self.NextToken() |
return result |
@@ -520,14 +481,12 @@ |
Raises: |
ParseError: If a boolean value couldn't be consumed. |
""" |
- if self.token in ('true', 't', '1'): |
- self.NextToken() |
- return True |
- elif self.token in ('false', 'f', '0'): |
- self.NextToken() |
- return False |
- else: |
- raise self._ParseError('Expected "true" or "false".') |
+ try: |
+ result = ParseBool(self.token) |
+ except ValueError, e: |
+ raise self._ParseError(str(e)) |
+ self.NextToken() |
+ return result |
def ConsumeString(self): |
"""Consumes a string value. |
@@ -567,7 +526,7 @@ |
""" |
text = self.token |
if len(text) < 1 or text[0] not in ('\'', '"'): |
- raise self._ParseError('Exptected string.') |
+ raise self._ParseError('Expected string.') |
if len(text) < 2 or text[-1] != text[0]: |
raise self._ParseError('String missing ending quote.') |
@@ -579,36 +538,12 @@ |
self.NextToken() |
return result |
- def _ParseInteger(self, text, is_signed=False, is_long=False): |
- """Parses an integer. |
- |
- Args: |
- text: The text to parse. |
- is_signed: True if a signed integer must be parsed. |
- is_long: True if a long integer must be parsed. |
- |
- Returns: |
- The integer value. |
- |
- Raises: |
- ValueError: Thrown Iff the text is not a valid integer. |
- """ |
- pos = 0 |
- if text.startswith('-'): |
- pos += 1 |
- |
- base = 10 |
- if text.startswith('0x', pos) or text.startswith('0X', pos): |
- base = 16 |
- elif text.startswith('0', pos): |
- base = 8 |
- |
- # Do the actual parsing. Exception handling is propagated to caller. |
- result = int(text, base) |
- |
- # Check if the integer is sane. Exceptions handled by callers. |
- checker = self._INTEGER_CHECKERS[2 * int(is_long) + int(is_signed)] |
- checker.CheckValue(result) |
+ def ConsumeEnum(self, field): |
+ try: |
+ result = ParseEnum(field, self.token) |
+ except ValueError, e: |
+ raise self._ParseError(str(e)) |
+ self.NextToken() |
return result |
def ParseErrorPreviousToken(self, message): |
@@ -626,14 +561,8 @@ |
def _ParseError(self, message): |
"""Creates and *returns* a ParseError for the current token.""" |
return ParseError('%d:%d : %s' % ( |
- self._line + 1, self._column - len(self.token) + 1, message)) |
+ self._line + 1, self._column + 1, message)) |
- def _IntegerParseError(self, e): |
- return self._ParseError('Couldn\'t parse integer: ' + str(e)) |
- |
- def _FloatParseError(self, e): |
- return self._ParseError('Couldn\'t parse number: ' + str(e)) |
- |
def _StringParseError(self, e): |
return self._ParseError('Couldn\'t parse string: ' + str(e)) |
@@ -689,3 +618,117 @@ |
# allow single-digit hex escapes (like '\xf'). |
result = _CUNESCAPE_HEX.sub(ReplaceHex, text) |
return result.decode('string_escape') |
+ |
+ |
+def ParseInteger(text, is_signed=False, is_long=False): |
+ """Parses an integer. |
+ |
+ Args: |
+ text: The text to parse. |
+ is_signed: True if a signed integer must be parsed. |
+ is_long: True if a long integer must be parsed. |
+ |
+ Returns: |
+ The integer value. |
+ |
+ Raises: |
+ ValueError: Thrown Iff the text is not a valid integer. |
+ """ |
+ # Do the actual parsing. Exception handling is propagated to caller. |
+ try: |
+ result = int(text, 0) |
+ except ValueError: |
+ raise ValueError('Couldn\'t parse integer: %s' % text) |
+ |
+ # Check if the integer is sane. Exceptions handled by callers. |
+ checker = _INTEGER_CHECKERS[2 * int(is_long) + int(is_signed)] |
+ checker.CheckValue(result) |
+ return result |
+ |
+ |
+def ParseFloat(text): |
+ """Parse a floating point number. |
+ |
+ Args: |
+ text: Text to parse. |
+ |
+ Returns: |
+ The number parsed. |
+ |
+ Raises: |
+ ValueError: If a floating point number couldn't be parsed. |
+ """ |
+ try: |
+ # Assume Python compatible syntax. |
+ return float(text) |
+ except ValueError: |
+ # Check alternative spellings. |
+ if _FLOAT_INFINITY.match(text): |
+ if text[0] == '-': |
+ return float('-inf') |
+ else: |
+ return float('inf') |
+ elif _FLOAT_NAN.match(text): |
+ return float('nan') |
+ else: |
+ # assume '1.0f' format |
+ try: |
+ return float(text.rstrip('f')) |
+ except ValueError: |
+ raise ValueError('Couldn\'t parse float: %s' % text) |
+ |
+ |
+def ParseBool(text): |
+ """Parse a boolean value. |
+ |
+ Args: |
+ text: Text to parse. |
+ |
+ Returns: |
+ Boolean values parsed |
+ |
+ Raises: |
+ ValueError: If text is not a valid boolean. |
+ """ |
+ if text in ('true', 't', '1'): |
+ return True |
+ elif text in ('false', 'f', '0'): |
+ return False |
+ else: |
+ raise ValueError('Expected "true" or "false".') |
+ |
+ |
+def ParseEnum(field, value): |
+ """Parse an enum value. |
+ |
+ The value can be specified by a number (the enum value), or by |
+ a string literal (the enum name). |
+ |
+ Args: |
+ field: Enum field descriptor. |
+ value: String value. |
+ |
+ Returns: |
+ Enum value number. |
+ |
+ Raises: |
+ ValueError: If the enum value could not be parsed. |
+ """ |
+ enum_descriptor = field.enum_type |
+ try: |
+ number = int(value, 0) |
+ except ValueError: |
+ # Identifier. |
+ enum_value = enum_descriptor.values_by_name.get(value, None) |
+ if enum_value is None: |
+ raise ValueError( |
+ 'Enum type "%s" has no value named %s.' % ( |
+ enum_descriptor.full_name, value)) |
+ else: |
+ # Numeric value. |
+ enum_value = enum_descriptor.values_by_number.get(number, None) |
+ if enum_value is None: |
+ raise ValueError( |
+ 'Enum type "%s" has no value with number %d.' % ( |
+ enum_descriptor.full_name, number)) |
+ return enum_value.number |