Team Ai
Datasetpublic

codekingpro/portable-devtools

sourceHugging Faceupdated 5mo agoView on Hugging Face
1likes14kdownloads
json_format.py1115 linesDownload Raw Back to protobuf
1# Protocol Buffers - Google's data interchange format2# Copyright 2008 Google Inc.  All rights reserved.3#4# Use of this source code is governed by a BSD-style5# license that can be found in the LICENSE file or at6# https://developers.google.com/open-source/licenses/bsd7 8"""Contains routines for printing protocol messages in JSON format.9 10Simple usage example:11 12  # Create a proto object and serialize it to a json format string.13  message = my_proto_pb2.MyMessage(foo='bar')14  json_string = json_format.MessageToJson(message)15 16  # Parse a json format string to proto object.17  message = json_format.Parse(json_string, my_proto_pb2.MyMessage())18"""19 20__author__ = 'jieluo@google.com (Jie Luo)'21 22 23import base6424from collections import OrderedDict25import json26import math27from operator import methodcaller28import re29import warnings30 31from google.protobuf import descriptor32from google.protobuf import message_factory33from google.protobuf import symbol_database34from google.protobuf.internal import type_checkers35 36 37_INT_TYPES = frozenset([38    descriptor.FieldDescriptor.CPPTYPE_INT32,39    descriptor.FieldDescriptor.CPPTYPE_UINT32,40    descriptor.FieldDescriptor.CPPTYPE_INT64,41    descriptor.FieldDescriptor.CPPTYPE_UINT64,42])43_INT64_TYPES = frozenset([44    descriptor.FieldDescriptor.CPPTYPE_INT64,45    descriptor.FieldDescriptor.CPPTYPE_UINT64,46])47_FLOAT_TYPES = frozenset([48    descriptor.FieldDescriptor.CPPTYPE_FLOAT,49    descriptor.FieldDescriptor.CPPTYPE_DOUBLE,50])51_INFINITY = 'Infinity'52_NEG_INFINITY = '-Infinity'53_NAN = 'NaN'54 55_UNPAIRED_SURROGATE_PATTERN = re.compile(56    '[\ud800-\udbff](?![\udc00-\udfff])|(?<![\ud800-\udbff])[\udc00-\udfff]'57)58 59_VALID_EXTENSION_NAME = re.compile(r'\[[a-zA-Z0-9\._]*\]$')60 61 62class Error(Exception):63  """Top-level module error for json_format."""64 65 66class SerializeToJsonError(Error):67  """Thrown if serialization to JSON fails."""68 69 70class ParseError(Error):71  """Thrown in case of parsing error."""72 73 74class EnumStringValueParseError(ParseError):75  """Thrown if unknown string enum value is encountered.76 77  This exception is suppressed if ignore_unknown_fields is set.78  """79 80 81def MessageToJson(82    message,83    preserving_proto_field_name=False,84    indent=2,85    sort_keys=False,86    use_integers_for_enums=False,87    descriptor_pool=None,88    float_precision=None,89    ensure_ascii=True,90    always_print_fields_with_no_presence=False,91):92  """Converts protobuf message to JSON format.93 94  Args:95    message: The protocol buffers message instance to serialize.96    always_print_fields_with_no_presence: If True, fields without presence97      (implicit presence scalars, repeated fields, and map fields) will always98      be serialized. Any field that supports presence is not affected by this99      option (including singular message fields and oneof fields).100    preserving_proto_field_name: If True, use the original proto field names as101      defined in the .proto file. If False, convert the field names to102      lowerCamelCase.103    indent: The JSON object will be pretty-printed with this indent level. An104      indent level of 0 or negative will only insert newlines. If the indent105      level is None, no newlines will be inserted.106    sort_keys: If True, then the output will be sorted by field names.107    use_integers_for_enums: If true, print integers instead of enum names.108    descriptor_pool: A Descriptor Pool for resolving types. If None use the109      default.110    float_precision: Deprecated. If set, use this to specify float field valid111      digits.112    ensure_ascii: If True, strings with non-ASCII characters are escaped. If113      False, Unicode strings are returned unchanged.114 115  Returns:116    A string containing the JSON formatted protocol buffer message.117  """118  printer = _Printer(119      preserving_proto_field_name,120      use_integers_for_enums,121      descriptor_pool,122      float_precision,123      always_print_fields_with_no_presence,124  )125  return printer.ToJsonString(message, indent, sort_keys, ensure_ascii)126 127 128def MessageToDict(129    message,130    always_print_fields_with_no_presence=False,131    preserving_proto_field_name=False,132    use_integers_for_enums=False,133    descriptor_pool=None,134    float_precision=None,135):136  """Converts protobuf message to a dictionary.137 138  When the dictionary is encoded to JSON, it conforms to proto3 JSON spec.139 140  Args:141    message: The protocol buffers message instance to serialize.142    always_print_fields_with_no_presence: If True, fields without presence143      (implicit presence scalars, repeated fields, and map fields) will always144      be serialized. Any field that supports presence is not affected by this145      option (including singular message fields and oneof fields).146    preserving_proto_field_name: If True, use the original proto field names as147      defined in the .proto file. If False, convert the field names to148      lowerCamelCase.149    use_integers_for_enums: If true, print integers instead of enum names.150    descriptor_pool: A Descriptor Pool for resolving types. If None use the151      default.152    float_precision: Deprecated. If set, use this to specify float field valid153      digits.154 155  Returns:156    A dict representation of the protocol buffer message.157  """158  printer = _Printer(159      preserving_proto_field_name,160      use_integers_for_enums,161      descriptor_pool,162      float_precision,163      always_print_fields_with_no_presence,164  )165  # pylint: disable=protected-access166  return printer._MessageToJsonObject(message)167 168 169def _IsMapEntry(field):170  return (171      field.type == descriptor.FieldDescriptor.TYPE_MESSAGE172      and field.message_type.has_options173      and field.message_type.GetOptions().map_entry174  )175 176 177class _Printer(object):178  """JSON format printer for protocol message."""179 180  def __init__(181      self,182      preserving_proto_field_name=False,183      use_integers_for_enums=False,184      descriptor_pool=None,185      float_precision=None,186      always_print_fields_with_no_presence=False,187  ):188    self.always_print_fields_with_no_presence = (189        always_print_fields_with_no_presence190    )191    self.preserving_proto_field_name = preserving_proto_field_name192    self.use_integers_for_enums = use_integers_for_enums193    self.descriptor_pool = descriptor_pool194    if float_precision:195      warnings.warn(196          'float_precision option is deprecated for json_format. '197          'This will turn into error in 7.34.0, please remove it '198          'before that.'199      )200      self.float_format = '.{}g'.format(float_precision)201    else:202      self.float_format = None203 204  def ToJsonString(self, message, indent, sort_keys, ensure_ascii):205    js = self._MessageToJsonObject(message)206    return json.dumps(207        js, indent=indent, sort_keys=sort_keys, ensure_ascii=ensure_ascii208    )209 210  def _MessageToJsonObject(self, message):211    """Converts message to an object according to Proto3 JSON Specification."""212    message_descriptor = message.DESCRIPTOR213    full_name = message_descriptor.full_name214    if _IsWrapperMessage(message_descriptor):215      return self._WrapperMessageToJsonObject(message)216    if full_name in _WKTJSONMETHODS:217      return methodcaller(_WKTJSONMETHODS[full_name][0], message)(self)218    js = {}219    return self._RegularMessageToJsonObject(message, js)220 221  def _RegularMessageToJsonObject(self, message, js):222    """Converts normal message according to Proto3 JSON Specification."""223    fields = message.ListFields()224 225    try:226      for field, value in fields:227        if field.is_extension:228          name = '[%s]' % field.full_name229        elif self.preserving_proto_field_name:230          name = field.name231        else:232          name = field.json_name233 234        if _IsMapEntry(field):235          # Convert a map field.236          v_field = field.message_type.fields_by_name['value']237          js_map = {}238          for key in value:239            if isinstance(key, bool):240              if key:241                recorded_key = 'true'242              else:243                recorded_key = 'false'244            else:245              recorded_key = str(key)246            js_map[recorded_key] = self._FieldToJsonObject(v_field, value[key])247          js[name] = js_map248        elif field.is_repeated:249          # Convert a repeated field.250          js[name] = [self._FieldToJsonObject(field, k) for k in value]251        else:252          js[name] = self._FieldToJsonObject(field, value)253 254      # Serialize default value if including_default_value_fields is True.255      if (256          self.always_print_fields_with_no_presence257      ):258        message_descriptor = message.DESCRIPTOR259        for field in message_descriptor.fields:260 261          # always_print_fields_with_no_presence doesn't apply to262          # any field which supports presence.263          if self.always_print_fields_with_no_presence and field.has_presence:264            continue265 266          if self.preserving_proto_field_name:267            name = field.name268          else:269            name = field.json_name270          if name in js:271            # Skip the field which has been serialized already.272            continue273          if _IsMapEntry(field):274            js[name] = {}275          elif field.is_repeated:276            js[name] = []277          else:278            js[name] = self._FieldToJsonObject(field, field.default_value)279 280    except ValueError as e:281      raise SerializeToJsonError(282          'Failed to serialize {0} field: {1}.'.format(field.name, e)283      ) from e284 285    return js286 287  def _FieldToJsonObject(self, field, value):288    """Converts field value according to Proto3 JSON Specification."""289    if field.cpp_type == descriptor.FieldDescriptor.CPPTYPE_MESSAGE:290      return self._MessageToJsonObject(value)291    elif field.cpp_type == descriptor.FieldDescriptor.CPPTYPE_ENUM:292      if self.use_integers_for_enums:293        return value294      if field.enum_type.full_name == 'google.protobuf.NullValue':295        return None296      enum_value = field.enum_type.values_by_number.get(value, None)297      if enum_value is not None:298        return enum_value.name299      else:300        if field.enum_type.is_closed:301          raise SerializeToJsonError(302              'Enum field contains an integer value '303              'which can not mapped to an enum value.'304          )305        else:306          return value307    elif field.cpp_type == descriptor.FieldDescriptor.CPPTYPE_STRING:308      if field.type == descriptor.FieldDescriptor.TYPE_BYTES:309        # Use base64 Data encoding for bytes310        return base64.b64encode(value).decode('utf-8')311      else:312        return str(value)313    elif field.cpp_type == descriptor.FieldDescriptor.CPPTYPE_BOOL:314      return bool(value)315    elif field.cpp_type in _INT64_TYPES:316      return str(value)317    elif field.cpp_type in _FLOAT_TYPES:318      if math.isinf(value):319        if value < 0.0:320          return _NEG_INFINITY321        else:322          return _INFINITY323      if math.isnan(value):324        return _NAN325      if self.float_format:326        return float(format(value, self.float_format))327      elif field.cpp_type == descriptor.FieldDescriptor.CPPTYPE_FLOAT:328        return type_checkers.ToShortestFloat(value)329 330    return value331 332  def _AnyMessageToJsonObject(self, message):333    """Converts Any message according to Proto3 JSON Specification."""334    if not message.ListFields():335      return {}336    # Must print @type first, use OrderedDict instead of {}337    js = OrderedDict()338    type_url = message.type_url339    js['@type'] = type_url340    sub_message = _CreateMessageFromTypeUrl(type_url, self.descriptor_pool)341    sub_message.ParseFromString(message.value)342    message_descriptor = sub_message.DESCRIPTOR343    full_name = message_descriptor.full_name344    if _IsWrapperMessage(message_descriptor):345      js['value'] = self._WrapperMessageToJsonObject(sub_message)346      return js347    if full_name in _WKTJSONMETHODS:348      js['value'] = methodcaller(_WKTJSONMETHODS[full_name][0], sub_message)(349          self350      )351      return js352    return self._RegularMessageToJsonObject(sub_message, js)353 354  def _GenericMessageToJsonObject(self, message):355    """Converts message according to Proto3 JSON Specification."""356    # Duration, Timestamp and FieldMask have ToJsonString method to do the357    # convert. Users can also call the method directly.358    return message.ToJsonString()359 360  def _ValueMessageToJsonObject(self, message):361    """Converts Value message according to Proto3 JSON Specification."""362    which = message.WhichOneof('kind')363    # If the Value message is not set treat as null_value when serialize364    # to JSON. The parse back result will be different from original message.365    if which is None or which == 'null_value':366      return None367    if which == 'list_value':368      return self._ListValueMessageToJsonObject(message.list_value)369    if which == 'number_value':370      value = message.number_value371      if math.isinf(value):372        raise ValueError(373            'Fail to serialize Infinity for Value.number_value, '374            'which would parse as string_value'375        )376      if math.isnan(value):377        raise ValueError(378            'Fail to serialize NaN for Value.number_value, '379            'which would parse as string_value'380        )381    else:382      value = getattr(message, which)383    oneof_descriptor = message.DESCRIPTOR.fields_by_name[which]384    return self._FieldToJsonObject(oneof_descriptor, value)385 386  def _ListValueMessageToJsonObject(self, message):387    """Converts ListValue message according to Proto3 JSON Specification."""388    return [self._ValueMessageToJsonObject(value) for value in message.values]389 390  def _StructMessageToJsonObject(self, message):391    """Converts Struct message according to Proto3 JSON Specification."""392    fields = message.fields393    ret = {}394    for key in fields:395      ret[key] = self._ValueMessageToJsonObject(fields[key])396    return ret397 398  def _WrapperMessageToJsonObject(self, message):399    return self._FieldToJsonObject(400        message.DESCRIPTOR.fields_by_name['value'], message.value401    )402 403 404def _IsWrapperMessage(message_descriptor):405  return message_descriptor.file.name == 'google/protobuf/wrappers.proto'406 407 408def _DuplicateChecker(js):409  result = {}410  for name, value in js:411    if name in result:412      raise ParseError('Failed to load JSON: duplicate key {0}.'.format(name))413    result[name] = value414  return result415 416 417def _CreateMessageFromTypeUrl(type_url, descriptor_pool):418  """Creates a message from a type URL."""419  db = symbol_database.Default()420  pool = db.pool if descriptor_pool is None else descriptor_pool421  type_name = type_url.split('/')[-1]422  try:423    message_descriptor = pool.FindMessageTypeByName(type_name)424  except KeyError as e:425    raise TypeError(426        'Can not find message descriptor by type_url: {0}'.format(type_url)427    ) from e428  message_class = message_factory.GetMessageClass(message_descriptor)429  return message_class()430 431 432def Parse(433    text,434    message,435    ignore_unknown_fields=False,436    descriptor_pool=None,437    max_recursion_depth=100,438):439  """Parses a JSON representation of a protocol message into a message.440 441  Args:442    text: Message JSON representation.443    message: A protocol buffer message to merge into.444    ignore_unknown_fields: If True, do not raise errors for unknown fields.445    descriptor_pool: A Descriptor Pool for resolving types. If None use the446      default.447    max_recursion_depth: max recursion depth of JSON message to be deserialized.448      JSON messages over this depth will fail to be deserialized. Default value449      is 100.450 451  Returns:452    The same message passed as argument.453 454  Raises::455    ParseError: On JSON parsing problems.456  """457  if not isinstance(text, str):458    text = text.decode('utf-8')459 460  try:461    js = json.loads(text, object_pairs_hook=_DuplicateChecker)462  except Exception as e:463    raise ParseError('Failed to load JSON: {0}.'.format(str(e))) from e464 465  try:466    return ParseDict(467        js, message, ignore_unknown_fields, descriptor_pool, max_recursion_depth468    )469  except ParseError as e:470    raise e471  except Exception as e:472    raise ParseError(473        'Failed to parse JSON: {0}: {1}.'.format(type(e).__name__, str(e))474    ) from e475 476 477def ParseDict(478    js_dict,479    message,480    ignore_unknown_fields=False,481    descriptor_pool=None,482    max_recursion_depth=100,483):484  """Parses a JSON dictionary representation into a message.485 486  Args:487    js_dict: Dict representation of a JSON message.488    message: A protocol buffer message to merge into.489    ignore_unknown_fields: If True, do not raise errors for unknown fields.490    descriptor_pool: A Descriptor Pool for resolving types. If None use the491      default.492    max_recursion_depth: max recursion depth of JSON message to be deserialized.493      JSON messages over this depth will fail to be deserialized. Default value494      is 100.495 496  Returns:497    The same message passed as argument.498  """499  parser = _Parser(ignore_unknown_fields, descriptor_pool, max_recursion_depth)500  parser.ConvertMessage(js_dict, message, '')501  return message502 503 504_INT_OR_FLOAT = (int, float)505_LIST_LIKE = (list, tuple)506 507 508class _Parser(object):509  """JSON format parser for protocol message."""510 511  def __init__(512      self, ignore_unknown_fields, descriptor_pool, max_recursion_depth513  ):514    self.ignore_unknown_fields = ignore_unknown_fields515    self.descriptor_pool = descriptor_pool516    self.max_recursion_depth = max_recursion_depth517    self.recursion_depth = 0518 519  def ConvertMessage(self, value, message, path):520    """Convert a JSON object into a message.521 522    Args:523      value: A JSON object.524      message: A WKT or regular protocol message to record the data.525      path: parent path to log parse error info.526 527    Raises:528      ParseError: In case of convert problems.529    """530    # Increment recursion depth at message entry. The max_recursion_depth limit531    # is exclusive: a depth value equal to max_recursion_depth will trigger an532    # error. For example, with max_recursion_depth=5, nesting up to depth 4 is533    # allowed, but attempting depth 5 raises ParseError.534    self.recursion_depth += 1535    if self.recursion_depth > self.max_recursion_depth:536      raise ParseError(537          'Message too deep. Max recursion depth is {0}'.format(538              self.max_recursion_depth539          )540      )541    message_descriptor = message.DESCRIPTOR542    full_name = message_descriptor.full_name543    if not path:544      path = message_descriptor.name545    if _IsWrapperMessage(message_descriptor):546      self._ConvertWrapperMessage(value, message, path)547    elif full_name in _WKTJSONMETHODS:548      methodcaller(_WKTJSONMETHODS[full_name][1], value, message, path)(self)549    else:550      self._ConvertFieldValuePair(value, message, path)551    self.recursion_depth -= 1552 553  def _ConvertFieldValuePair(self, js, message, path):554    """Convert field value pairs into regular message.555 556    Args:557      js: A JSON object to convert the field value pairs.558      message: A regular protocol message to record the data.559      path: parent path to log parse error info.560 561    Raises:562      ParseError: In case of problems converting.563    """564    names = []565    message_descriptor = message.DESCRIPTOR566    fields_by_json_name = dict(567        (f.json_name, f) for f in message_descriptor.fields568    )569 570    def _ClearFieldOrExtension(message, field):571      if field.is_extension:572        message.ClearExtension(field)573      else:574        message.ClearField(field.name)575 576    def _GetFieldOrExtension(message, field):577      if field.is_extension:578        return message.Extensions[field]579      else:580        return getattr(message, field.name)581 582    def _SetFieldOrExtension(message, field, value):583      if field.is_extension:584        message.Extensions[field] = value585      else:586        setattr(message, field.name, value)587 588    for name in js:589      try:590        field = fields_by_json_name.get(name, None)591        if not field:592          field = message_descriptor.fields_by_name.get(name, None)593        if not field and _VALID_EXTENSION_NAME.match(name):594          if not message_descriptor.is_extendable:595            raise ParseError(596                'Message type {0} does not have extensions at {1}'.format(597                    message_descriptor.full_name, path598                )599            )600          identifier = name[1:-1]  # strip [] brackets601          # pylint: disable=protected-access602          field = message.Extensions._FindExtensionByName(identifier)603          # pylint: enable=protected-access604          if not field:605            # Try looking for extension by the message type name, dropping the606            # field name following the final . separator in full_name.607            identifier = '.'.join(identifier.split('.')[:-1])608            # pylint: disable=protected-access609            field = message.Extensions._FindExtensionByName(identifier)610            # pylint: enable=protected-access611        if not field:612          if self.ignore_unknown_fields:613            continue614          raise ParseError(615              (616                  'Message type "{0}" has no field named "{1}" at "{2}".\n'617                  ' Available Fields(except extensions): "{3}"'618              ).format(619                  message_descriptor.full_name,620                  name,621                  path,622                  [f.json_name for f in message_descriptor.fields],623              )624          )625        if name in names:626          raise ParseError(627              'Message type "{0}" should not have multiple '628              '"{1}" fields at "{2}".'.format(629                  message.DESCRIPTOR.full_name, name, path630              )631          )632        names.append(name)633        value = js[name]634        # Check no other oneof field is parsed.635        if field.containing_oneof is not None and value is not None:636          oneof_name = field.containing_oneof.name637          if oneof_name in names:638            raise ParseError(639                'Message type "{0}" should not have multiple '640                '"{1}" oneof fields at "{2}".'.format(641                    message.DESCRIPTOR.full_name, oneof_name, path642                )643            )644          names.append(oneof_name)645 646        if value is None:647          if (648              field.cpp_type == descriptor.FieldDescriptor.CPPTYPE_MESSAGE649              and field.message_type.full_name == 'google.protobuf.Value'650          ):651            sub_message = _GetFieldOrExtension(message, field)652            sub_message.null_value = 0653          elif (654              field.cpp_type == descriptor.FieldDescriptor.CPPTYPE_ENUM655              and field.enum_type.full_name == 'google.protobuf.NullValue'656          ):657            _SetFieldOrExtension(message, field, 0)658          else:659            _ClearFieldOrExtension(message, field)660          continue661 662        # Parse field value.663        if _IsMapEntry(field):664          _ClearFieldOrExtension(message, field)665          self._ConvertMapFieldValue(666              value, message, field, '{0}.{1}'.format(path, name)667          )668        elif field.is_repeated:669          _ClearFieldOrExtension(message, field)670          if not isinstance(value, _LIST_LIKE):671            raise ParseError(672                'repeated field {0} must be in [] which is {1} at {2}'.format(673                    name, value, path674                )675            )676          if field.cpp_type == descriptor.FieldDescriptor.CPPTYPE_MESSAGE:677            # Repeated message field.678            for index, item in enumerate(value):679              sub_message = _GetFieldOrExtension(message, field).add()680              # None is a null_value in Value.681              if (682                  item is None683                  and sub_message.DESCRIPTOR.full_name684                  != 'google.protobuf.Value'685              ):686                raise ParseError(687                    'null is not allowed to be used as an element'688                    ' in a repeated field at {0}.{1}[{2}]'.format(689                        path, name, index690                    )691                )692              self.ConvertMessage(693                  item, sub_message, '{0}.{1}[{2}]'.format(path, name, index)694              )695          else:696            # Repeated scalar field.697            for index, item in enumerate(value):698              if item is None:699                raise ParseError(700                    'null is not allowed to be used as an element'701                    ' in a repeated field at {0}.{1}[{2}]'.format(702                        path, name, index703                    )704                )705              self._ConvertAndAppendScalar(706                  message, field, item, '{0}.{1}[{2}]'.format(path, name, index)707              )708        elif field.cpp_type == descriptor.FieldDescriptor.CPPTYPE_MESSAGE:709          sub_message = _GetFieldOrExtension(message, field)710          sub_message.SetInParent()711          self.ConvertMessage(value, sub_message, '{0}.{1}'.format(path, name))712        else:713          self._ConvertAndSetScalar(714              message, field, value, '{0}.{1}'.format(path, name)715          )716      except ParseError as e:717        if field and field.containing_oneof is None:718          raise ParseError(719              'Failed to parse {0} field: {1}.'.format(name, e)720          ) from e721        else:722          raise ParseError(str(e)) from e723      except ValueError as e:724        raise ParseError(725            'Failed to parse {0} field: {1}.'.format(name, e)726        ) from e727      except TypeError as e:728        raise ParseError(729            'Failed to parse {0} field: {1}.'.format(name, e)730        ) from e731 732  def _ConvertAnyMessage(self, value, message, path):733    """Convert a JSON representation into Any message."""734    if isinstance(value, dict) and not value:735      return736    try:737      type_url = value['@type']738    except KeyError as e:739      raise ParseError(740          '@type is missing when parsing any message at {0}'.format(path)741      ) from e742 743    try:744      sub_message = _CreateMessageFromTypeUrl(type_url, self.descriptor_pool)745    except TypeError as e:746      raise ParseError('{0} at {1}'.format(e, path)) from e747    message_descriptor = sub_message.DESCRIPTOR748    full_name = message_descriptor.full_name749    if _IsWrapperMessage(message_descriptor):750      self._ConvertWrapperMessage(751          value['value'], sub_message, '{0}.value'.format(path)752      )753    elif full_name in _WKTJSONMETHODS:754      # For well-known types (including nested Any), use ConvertMessage755      # to ensure recursion depth is properly tracked756      self.ConvertMessage(757          value['value'], sub_message, '{0}.value'.format(path)758      )759    else:760      del value['@type']761      try:762        self._ConvertFieldValuePair(value, sub_message, path)763      finally:764        value['@type'] = type_url765    # Sets Any message766    message.value = sub_message.SerializeToString()767    message.type_url = type_url768 769  def _ConvertGenericMessage(self, value, message, path):770    """Convert a JSON representation into message with FromJsonString."""771    # Duration, Timestamp, FieldMask have a FromJsonString method to do the772    # conversion. Users can also call the method directly.773    try:774      message.FromJsonString(value)775    except ValueError as e:776      raise ParseError('{0} at {1}'.format(e, path)) from e777 778  def _ConvertValueMessage(self, value, message, path):779    """Convert a JSON representation into Value message."""780    if isinstance(value, dict):781      self._ConvertStructMessage(value, message.struct_value, path)782    elif isinstance(value, _LIST_LIKE):783      self._ConvertListOrTupleValueMessage(value, message.list_value, path)784    elif value is None:785      message.null_value = 0786    elif isinstance(value, bool):787      message.bool_value = value788    elif isinstance(value, str):789      message.string_value = value790    elif isinstance(value, _INT_OR_FLOAT):791      message.number_value = value792    else:793      raise ParseError(794          'Value {0} has unexpected type {1} at {2}'.format(795              value, type(value), path796          )797      )798 799  def _ConvertListOrTupleValueMessage(self, value, message, path):800    """Convert a JSON representation into ListValue message."""801    if not isinstance(value, _LIST_LIKE):802      raise ParseError(803          'ListValue must be in [] which is {0} at {1}'.format(value, path)804      )805    message.ClearField('values')806    for index, item in enumerate(value):807      self._ConvertValueMessage(808          item, message.values.add(), '{0}[{1}]'.format(path, index)809      )810 811  def _ConvertStructMessage(self, value, message, path):812    """Convert a JSON representation into Struct message."""813    if not isinstance(value, dict):814      raise ParseError(815          'Struct must be in a dict which is {0} at {1}'.format(value, path)816      )817    # Clear will mark the struct as modified so it will be created even if818    # there are no values.819    message.Clear()820    for key in value:821      self._ConvertValueMessage(822          value[key], message.fields[key], '{0}.{1}'.format(path, key)823      )824    return825 826  def _ConvertWrapperMessage(self, value, message, path):827    """Convert a JSON representation into Wrapper message."""828    field = message.DESCRIPTOR.fields_by_name['value']829    self._ConvertAndSetScalar(830        message, field, value, path='{0}.value'.format(path)831    )832 833  def _ConvertMapFieldValue(self, value, message, field, path):834    """Convert map field value for a message map field.835 836    Args:837      value: A JSON object to convert the map field value.838      message: A protocol message to record the converted data.839      field: The descriptor of the map field to be converted.840      path: parent path to log parse error info.841 842    Raises:843      ParseError: In case of convert problems.844    """845    if not isinstance(value, dict):846      raise ParseError(847          'Map field {0} must be in a dict which is {1} at {2}'.format(848              field.name, value, path849          )850      )851    key_field = field.message_type.fields_by_name['key']852    value_field = field.message_type.fields_by_name['value']853    for key in value:854      key_value = _ConvertScalarFieldValue(855          key, key_field, '{0}.key'.format(path), True856      )857      if value_field.cpp_type == descriptor.FieldDescriptor.CPPTYPE_MESSAGE:858        self.ConvertMessage(859            value[key],860            getattr(message, field.name)[key_value],861            '{0}[{1}]'.format(path, key_value),862        )863      else:864        self._ConvertAndSetScalarToMapKey(865            message,866            field,867            key_value,868            value[key],869            path='{0}[{1}]'.format(path, key_value),870        )871 872  def _ConvertAndSetScalar(self, message, field, js_value, path):873    """Convert scalar from js_value and assign it to message.field."""874    try:875      value = _ConvertScalarFieldValue(js_value, field, path)876      if field.is_extension:877        message.Extensions[field] = value878      else:879        setattr(message, field.name, value)880    except EnumStringValueParseError:881      if not self.ignore_unknown_fields:882        raise883 884  def _ConvertAndAppendScalar(self, message, repeated_field, js_value, path):885    """Convert scalar from js_value and append it to message.repeated_field."""886    try:887      if repeated_field.is_extension:888        repeated = message.Extensions[repeated_field]889      else:890        repeated = getattr(message, repeated_field.name)891      value = _ConvertScalarFieldValue(js_value, repeated_field, path)892      repeated.append(value)893    except EnumStringValueParseError:894      if not self.ignore_unknown_fields:895        raise896 897  def _ConvertAndSetScalarToMapKey(898      self, message, map_field, converted_key, js_value, path899  ):900    """Convert scalar from 'js_value' and add it to message.map_field[converted_key]."""901    try:902      getattr(message, map_field.name)[converted_key] = (903          _ConvertScalarFieldValue(904              js_value,905              map_field.message_type.fields_by_name['value'],906              path,907          )908      )909    except EnumStringValueParseError:910      if not self.ignore_unknown_fields:911        raise912 913 914def _ConvertScalarFieldValue(value, field, path, require_str=False):915  """Convert a single scalar field value.916 917  Args:918    value: A scalar value to convert the scalar field value.919    field: The descriptor of the field to convert.920    path: parent path to log parse error info.921    require_str: If True, the field value must be a str.922 923  Returns:924    The converted scalar field value925 926  Raises:927    ParseError: In case of convert problems.928    EnumStringValueParseError: In case of unknown enum string value.929  """930  try:931    if field.cpp_type in _INT_TYPES:932      return _ConvertInteger(value)933    elif field.cpp_type in _FLOAT_TYPES:934      return _ConvertFloat(value, field)935    elif field.cpp_type == descriptor.FieldDescriptor.CPPTYPE_BOOL:936      return _ConvertBool(value, require_str)937    elif field.cpp_type == descriptor.FieldDescriptor.CPPTYPE_STRING:938      if field.type == descriptor.FieldDescriptor.TYPE_BYTES:939        if isinstance(value, str):940          encoded = value.encode('utf-8')941        else:942          encoded = value943        # Add extra padding '='944        padded_value = encoded + b'=' * (4 - len(encoded) % 4)945        return base64.urlsafe_b64decode(padded_value)946      else:947        # Checking for unpaired surrogates appears to be unreliable,948        # depending on the specific Python version, so we check manually.949        if _UNPAIRED_SURROGATE_PATTERN.search(value):950          raise ParseError('Unpaired surrogate')951        return value952    elif field.cpp_type == descriptor.FieldDescriptor.CPPTYPE_ENUM:953      # Convert an enum value.954      enum_value = field.enum_type.values_by_name.get(value, None)955      if enum_value is None:956        try:957          number = int(value)958          enum_value = field.enum_type.values_by_number.get(number, None)959        except ValueError as e:960          # Since parsing to integer failed and lookup in values_by_name didn't961          # find this name, we have an enum string value which is unknown.962          raise EnumStringValueParseError(963              'Invalid enum value {0} for enum type {1}'.format(964                  value, field.enum_type.full_name965              )966          ) from e967        if enum_value is None:968          if field.enum_type.is_closed:969            raise ParseError(970                'Invalid enum value {0} for enum type {1}'.format(971                    value, field.enum_type.full_name972                )973            )974          else:975            return number976      return enum_value.number977  except EnumStringValueParseError as e:978    raise EnumStringValueParseError('{0} at {1}'.format(e, path)) from e979  except ParseError as e:980    raise ParseError('{0} at {1}'.format(e, path)) from e981 982 983def _ConvertInteger(value):984  """Convert an integer.985 986  Args:987    value: A scalar value to convert.988 989  Returns:990    The integer value.991 992  Raises:993    ParseError: If an integer couldn't be consumed.994  """995  if isinstance(value, float) and not value.is_integer():996    raise ParseError("Couldn't parse integer: {0}".format(value))997 998  if isinstance(value, str) and value.find(' ') != -1:999    raise ParseError('Couldn\'t parse integer: "{0}"'.format(value))1000 1001  if isinstance(value, bool):1002    raise ParseError(1003        'Bool value {0} is not acceptable for integer field'.format(value)1004    )1005 1006  try:1007    return int(value)1008  except ValueError as e:1009    # Attempt to parse as an integer-valued float.1010    try:1011      f = float(value)1012    except ValueError:1013      # Raise the original exception for the int parse.1014      raise e  # pylint: disable=raise-missing-from1015    if not f.is_integer():1016      raise ParseError(1017          'Couldn\'t parse non-integer string: "{0}"'.format(value)1018      ) from e1019    return int(f)1020 1021 1022def _ConvertFloat(value, field):1023  """Convert an floating point number."""1024  if isinstance(value, float):1025    if math.isnan(value):1026      raise ParseError('Couldn\'t parse NaN, use quoted "NaN" instead')1027    if math.isinf(value):1028      if value > 0:1029        raise ParseError(1030            "Couldn't parse Infinity or value too large, "1031            'use quoted "Infinity" instead'1032        )1033      else:1034        raise ParseError(1035            "Couldn't parse -Infinity or value too small, "1036            'use quoted "-Infinity" instead'1037        )1038    if field.cpp_type == descriptor.FieldDescriptor.CPPTYPE_FLOAT:1039      # pylint: disable=protected-access1040      if value > type_checkers._FLOAT_MAX:1041        raise ParseError('Float value too large')1042      # pylint: disable=protected-access1043      if value < type_checkers._FLOAT_MIN:1044        raise ParseError('Float value too small')1045  if value == 'nan':1046    raise ParseError('Couldn\'t parse float "nan", use "NaN" instead')1047  try:1048    # Assume Python compatible syntax.1049    return float(value)1050  except ValueError as e:1051    # Check alternative spellings.1052    if value == _NEG_INFINITY:1053      return float('-inf')1054    elif value == _INFINITY:1055      return float('inf')1056    elif value == _NAN:1057      return float('nan')1058    else:1059      raise ParseError("Couldn't parse float: {0}".format(value)) from e1060 1061 1062def _ConvertBool(value, require_str):1063  """Convert a boolean value.1064 1065  Args:1066    value: A scalar value to convert.1067    require_str: If True, value must be a str.1068 1069  Returns:1070    The bool parsed.1071 1072  Raises:1073    ParseError: If a boolean value couldn't be consumed.1074  """1075  if require_str:1076    if value == 'true':1077      return True1078    elif value == 'false':1079      return False1080    else:1081      raise ParseError('Expected "true" or "false", not {0}'.format(value))1082 1083  if not isinstance(value, bool):1084    raise ParseError('Expected true or false without quotes')1085  return value1086 1087 1088_WKTJSONMETHODS = {1089    'google.protobuf.Any': ['_AnyMessageToJsonObject', '_ConvertAnyMessage'],1090    'google.protobuf.Duration': [1091        '_GenericMessageToJsonObject',1092        '_ConvertGenericMessage',1093    ],1094    'google.protobuf.FieldMask': [1095        '_GenericMessageToJsonObject',1096        '_ConvertGenericMessage',1097    ],1098    'google.protobuf.ListValue': [1099        '_ListValueMessageToJsonObject',1100        '_ConvertListOrTupleValueMessage',1101    ],1102    'google.protobuf.Struct': [1103        '_StructMessageToJsonObject',1104        '_ConvertStructMessage',1105    ],1106    'google.protobuf.Timestamp': [1107        '_GenericMessageToJsonObject',1108        '_ConvertGenericMessage',1109    ],1110    'google.protobuf.Value': [1111        '_ValueMessageToJsonObject',1112        '_ConvertValueMessage',1113    ],1114}1115 
codekingpro/portable-devtools · Team Ai