codekingpro/portable-devtools
114k
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"""Dynamic Protobuf class creator."""9 10from collections import OrderedDict11import hashlib12import os13 14from google.protobuf import descriptor_pb215from google.protobuf import descriptor16from google.protobuf import descriptor_pool17from google.protobuf import message_factory18 19 20def _GetMessageFromFactory(pool, full_name):21 """Get a proto class from the MessageFactory by name.22 23 Args:24 pool: a descriptor pool.25 full_name: str, the fully qualified name of the proto type.26 Returns:27 A class, for the type identified by full_name.28 Raises:29 KeyError, if the proto is not found in the factory's descriptor pool.30 """31 proto_descriptor = pool.FindMessageTypeByName(full_name)32 proto_cls = message_factory.GetMessageClass(proto_descriptor)33 return proto_cls34 35 36def MakeSimpleProtoClass(fields, full_name=None, pool=None):37 """Create a Protobuf class whose fields are basic types.38 39 Note: this doesn't validate field names!40 41 Args:42 fields: dict of {name: field_type} mappings for each field in the proto. If43 this is an OrderedDict the order will be maintained, otherwise the44 fields will be sorted by name.45 full_name: optional str, the fully-qualified name of the proto type.46 pool: optional DescriptorPool instance.47 Returns:48 a class, the new protobuf class with a FileDescriptor.49 """50 pool_instance = pool or descriptor_pool.DescriptorPool()51 if full_name is not None:52 try:53 proto_cls = _GetMessageFromFactory(pool_instance, full_name)54 return proto_cls55 except KeyError:56 # The factory's DescriptorPool doesn't know about this class yet.57 pass58 59 # Get a list of (name, field_type) tuples from the fields dict. If fields was60 # an OrderedDict we keep the order, but otherwise we sort the field to ensure61 # consistent ordering.62 field_items = fields.items()63 if not isinstance(fields, OrderedDict):64 field_items = sorted(field_items)65 66 # Use a consistent file name that is unlikely to conflict with any imported67 # proto files.68 fields_hash = hashlib.sha1()69 for f_name, f_type in field_items:70 fields_hash.update(f_name.encode('utf-8'))71 fields_hash.update(str(f_type).encode('utf-8'))72 proto_file_name = fields_hash.hexdigest() + '.proto'73 74 # If the proto is anonymous, use the same hash to name it.75 if full_name is None:76 full_name = ('net.proto2.python.public.proto_builder.AnonymousProto_' +77 fields_hash.hexdigest())78 try:79 proto_cls = _GetMessageFromFactory(pool_instance, full_name)80 return proto_cls81 except KeyError:82 # The factory's DescriptorPool doesn't know about this class yet.83 pass84 85 # This is the first time we see this proto: add a new descriptor to the pool.86 pool_instance.Add(87 _MakeFileDescriptorProto(proto_file_name, full_name, field_items))88 return _GetMessageFromFactory(pool_instance, full_name)89 90 91def _MakeFileDescriptorProto(proto_file_name, full_name, field_items):92 """Populate FileDescriptorProto for MessageFactory's DescriptorPool."""93 package, name = full_name.rsplit('.', 1)94 file_proto = descriptor_pb2.FileDescriptorProto()95 file_proto.name = os.path.join(package.replace('.', '/'), proto_file_name)96 file_proto.package = package97 desc_proto = file_proto.message_type.add()98 desc_proto.name = name99 for f_number, (f_name, f_type) in enumerate(field_items, 1):100 field_proto = desc_proto.field.add()101 field_proto.name = f_name102 # # If the number falls in the reserved range, reassign it to the correct103 # # number after the range.104 if f_number >= descriptor.FieldDescriptor.FIRST_RESERVED_FIELD_NUMBER:105 f_number += (106 descriptor.FieldDescriptor.LAST_RESERVED_FIELD_NUMBER -107 descriptor.FieldDescriptor.FIRST_RESERVED_FIELD_NUMBER + 1)108 field_proto.number = f_number109 field_proto.label = descriptor_pb2.FieldDescriptorProto.LABEL_OPTIONAL110 field_proto.type = f_type111 return file_proto112 