xref: /aosp_15_r20/external/protobuf/python/google/protobuf/proto_builder.py (revision 1b3f573f81763fcece89efc2b6a5209149e44ab8)
1*1b3f573fSAndroid Build Coastguard Worker# Protocol Buffers - Google's data interchange format
2*1b3f573fSAndroid Build Coastguard Worker# Copyright 2008 Google Inc.  All rights reserved.
3*1b3f573fSAndroid Build Coastguard Worker# https://developers.google.com/protocol-buffers/
4*1b3f573fSAndroid Build Coastguard Worker#
5*1b3f573fSAndroid Build Coastguard Worker# Redistribution and use in source and binary forms, with or without
6*1b3f573fSAndroid Build Coastguard Worker# modification, are permitted provided that the following conditions are
7*1b3f573fSAndroid Build Coastguard Worker# met:
8*1b3f573fSAndroid Build Coastguard Worker#
9*1b3f573fSAndroid Build Coastguard Worker#     * Redistributions of source code must retain the above copyright
10*1b3f573fSAndroid Build Coastguard Worker# notice, this list of conditions and the following disclaimer.
11*1b3f573fSAndroid Build Coastguard Worker#     * Redistributions in binary form must reproduce the above
12*1b3f573fSAndroid Build Coastguard Worker# copyright notice, this list of conditions and the following disclaimer
13*1b3f573fSAndroid Build Coastguard Worker# in the documentation and/or other materials provided with the
14*1b3f573fSAndroid Build Coastguard Worker# distribution.
15*1b3f573fSAndroid Build Coastguard Worker#     * Neither the name of Google Inc. nor the names of its
16*1b3f573fSAndroid Build Coastguard Worker# contributors may be used to endorse or promote products derived from
17*1b3f573fSAndroid Build Coastguard Worker# this software without specific prior written permission.
18*1b3f573fSAndroid Build Coastguard Worker#
19*1b3f573fSAndroid Build Coastguard Worker# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20*1b3f573fSAndroid Build Coastguard Worker# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21*1b3f573fSAndroid Build Coastguard Worker# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22*1b3f573fSAndroid Build Coastguard Worker# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
23*1b3f573fSAndroid Build Coastguard Worker# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24*1b3f573fSAndroid Build Coastguard Worker# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25*1b3f573fSAndroid Build Coastguard Worker# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26*1b3f573fSAndroid Build Coastguard Worker# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27*1b3f573fSAndroid Build Coastguard Worker# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28*1b3f573fSAndroid Build Coastguard Worker# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29*1b3f573fSAndroid Build Coastguard Worker# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30*1b3f573fSAndroid Build Coastguard Worker
31*1b3f573fSAndroid Build Coastguard Worker"""Dynamic Protobuf class creator."""
32*1b3f573fSAndroid Build Coastguard Worker
33*1b3f573fSAndroid Build Coastguard Workerfrom collections import OrderedDict
34*1b3f573fSAndroid Build Coastguard Workerimport hashlib
35*1b3f573fSAndroid Build Coastguard Workerimport os
36*1b3f573fSAndroid Build Coastguard Worker
37*1b3f573fSAndroid Build Coastguard Workerfrom google.protobuf import descriptor_pb2
38*1b3f573fSAndroid Build Coastguard Workerfrom google.protobuf import descriptor
39*1b3f573fSAndroid Build Coastguard Workerfrom google.protobuf import message_factory
40*1b3f573fSAndroid Build Coastguard Worker
41*1b3f573fSAndroid Build Coastguard Worker
42*1b3f573fSAndroid Build Coastguard Workerdef _GetMessageFromFactory(factory, full_name):
43*1b3f573fSAndroid Build Coastguard Worker  """Get a proto class from the MessageFactory by name.
44*1b3f573fSAndroid Build Coastguard Worker
45*1b3f573fSAndroid Build Coastguard Worker  Args:
46*1b3f573fSAndroid Build Coastguard Worker    factory: a MessageFactory instance.
47*1b3f573fSAndroid Build Coastguard Worker    full_name: str, the fully qualified name of the proto type.
48*1b3f573fSAndroid Build Coastguard Worker  Returns:
49*1b3f573fSAndroid Build Coastguard Worker    A class, for the type identified by full_name.
50*1b3f573fSAndroid Build Coastguard Worker  Raises:
51*1b3f573fSAndroid Build Coastguard Worker    KeyError, if the proto is not found in the factory's descriptor pool.
52*1b3f573fSAndroid Build Coastguard Worker  """
53*1b3f573fSAndroid Build Coastguard Worker  proto_descriptor = factory.pool.FindMessageTypeByName(full_name)
54*1b3f573fSAndroid Build Coastguard Worker  proto_cls = factory.GetPrototype(proto_descriptor)
55*1b3f573fSAndroid Build Coastguard Worker  return proto_cls
56*1b3f573fSAndroid Build Coastguard Worker
57*1b3f573fSAndroid Build Coastguard Worker
58*1b3f573fSAndroid Build Coastguard Workerdef MakeSimpleProtoClass(fields, full_name=None, pool=None):
59*1b3f573fSAndroid Build Coastguard Worker  """Create a Protobuf class whose fields are basic types.
60*1b3f573fSAndroid Build Coastguard Worker
61*1b3f573fSAndroid Build Coastguard Worker  Note: this doesn't validate field names!
62*1b3f573fSAndroid Build Coastguard Worker
63*1b3f573fSAndroid Build Coastguard Worker  Args:
64*1b3f573fSAndroid Build Coastguard Worker    fields: dict of {name: field_type} mappings for each field in the proto. If
65*1b3f573fSAndroid Build Coastguard Worker        this is an OrderedDict the order will be maintained, otherwise the
66*1b3f573fSAndroid Build Coastguard Worker        fields will be sorted by name.
67*1b3f573fSAndroid Build Coastguard Worker    full_name: optional str, the fully-qualified name of the proto type.
68*1b3f573fSAndroid Build Coastguard Worker    pool: optional DescriptorPool instance.
69*1b3f573fSAndroid Build Coastguard Worker  Returns:
70*1b3f573fSAndroid Build Coastguard Worker    a class, the new protobuf class with a FileDescriptor.
71*1b3f573fSAndroid Build Coastguard Worker  """
72*1b3f573fSAndroid Build Coastguard Worker  factory = message_factory.MessageFactory(pool=pool)
73*1b3f573fSAndroid Build Coastguard Worker
74*1b3f573fSAndroid Build Coastguard Worker  if full_name is not None:
75*1b3f573fSAndroid Build Coastguard Worker    try:
76*1b3f573fSAndroid Build Coastguard Worker      proto_cls = _GetMessageFromFactory(factory, full_name)
77*1b3f573fSAndroid Build Coastguard Worker      return proto_cls
78*1b3f573fSAndroid Build Coastguard Worker    except KeyError:
79*1b3f573fSAndroid Build Coastguard Worker      # The factory's DescriptorPool doesn't know about this class yet.
80*1b3f573fSAndroid Build Coastguard Worker      pass
81*1b3f573fSAndroid Build Coastguard Worker
82*1b3f573fSAndroid Build Coastguard Worker  # Get a list of (name, field_type) tuples from the fields dict. If fields was
83*1b3f573fSAndroid Build Coastguard Worker  # an OrderedDict we keep the order, but otherwise we sort the field to ensure
84*1b3f573fSAndroid Build Coastguard Worker  # consistent ordering.
85*1b3f573fSAndroid Build Coastguard Worker  field_items = fields.items()
86*1b3f573fSAndroid Build Coastguard Worker  if not isinstance(fields, OrderedDict):
87*1b3f573fSAndroid Build Coastguard Worker    field_items = sorted(field_items)
88*1b3f573fSAndroid Build Coastguard Worker
89*1b3f573fSAndroid Build Coastguard Worker  # Use a consistent file name that is unlikely to conflict with any imported
90*1b3f573fSAndroid Build Coastguard Worker  # proto files.
91*1b3f573fSAndroid Build Coastguard Worker  fields_hash = hashlib.sha1()
92*1b3f573fSAndroid Build Coastguard Worker  for f_name, f_type in field_items:
93*1b3f573fSAndroid Build Coastguard Worker    fields_hash.update(f_name.encode('utf-8'))
94*1b3f573fSAndroid Build Coastguard Worker    fields_hash.update(str(f_type).encode('utf-8'))
95*1b3f573fSAndroid Build Coastguard Worker  proto_file_name = fields_hash.hexdigest() + '.proto'
96*1b3f573fSAndroid Build Coastguard Worker
97*1b3f573fSAndroid Build Coastguard Worker  # If the proto is anonymous, use the same hash to name it.
98*1b3f573fSAndroid Build Coastguard Worker  if full_name is None:
99*1b3f573fSAndroid Build Coastguard Worker    full_name = ('net.proto2.python.public.proto_builder.AnonymousProto_' +
100*1b3f573fSAndroid Build Coastguard Worker                 fields_hash.hexdigest())
101*1b3f573fSAndroid Build Coastguard Worker    try:
102*1b3f573fSAndroid Build Coastguard Worker      proto_cls = _GetMessageFromFactory(factory, full_name)
103*1b3f573fSAndroid Build Coastguard Worker      return proto_cls
104*1b3f573fSAndroid Build Coastguard Worker    except KeyError:
105*1b3f573fSAndroid Build Coastguard Worker      # The factory's DescriptorPool doesn't know about this class yet.
106*1b3f573fSAndroid Build Coastguard Worker      pass
107*1b3f573fSAndroid Build Coastguard Worker
108*1b3f573fSAndroid Build Coastguard Worker  # This is the first time we see this proto: add a new descriptor to the pool.
109*1b3f573fSAndroid Build Coastguard Worker  factory.pool.Add(
110*1b3f573fSAndroid Build Coastguard Worker      _MakeFileDescriptorProto(proto_file_name, full_name, field_items))
111*1b3f573fSAndroid Build Coastguard Worker  return _GetMessageFromFactory(factory, full_name)
112*1b3f573fSAndroid Build Coastguard Worker
113*1b3f573fSAndroid Build Coastguard Worker
114*1b3f573fSAndroid Build Coastguard Workerdef _MakeFileDescriptorProto(proto_file_name, full_name, field_items):
115*1b3f573fSAndroid Build Coastguard Worker  """Populate FileDescriptorProto for MessageFactory's DescriptorPool."""
116*1b3f573fSAndroid Build Coastguard Worker  package, name = full_name.rsplit('.', 1)
117*1b3f573fSAndroid Build Coastguard Worker  file_proto = descriptor_pb2.FileDescriptorProto()
118*1b3f573fSAndroid Build Coastguard Worker  file_proto.name = os.path.join(package.replace('.', '/'), proto_file_name)
119*1b3f573fSAndroid Build Coastguard Worker  file_proto.package = package
120*1b3f573fSAndroid Build Coastguard Worker  desc_proto = file_proto.message_type.add()
121*1b3f573fSAndroid Build Coastguard Worker  desc_proto.name = name
122*1b3f573fSAndroid Build Coastguard Worker  for f_number, (f_name, f_type) in enumerate(field_items, 1):
123*1b3f573fSAndroid Build Coastguard Worker    field_proto = desc_proto.field.add()
124*1b3f573fSAndroid Build Coastguard Worker    field_proto.name = f_name
125*1b3f573fSAndroid Build Coastguard Worker    # # If the number falls in the reserved range, reassign it to the correct
126*1b3f573fSAndroid Build Coastguard Worker    # # number after the range.
127*1b3f573fSAndroid Build Coastguard Worker    if f_number >= descriptor.FieldDescriptor.FIRST_RESERVED_FIELD_NUMBER:
128*1b3f573fSAndroid Build Coastguard Worker      f_number += (
129*1b3f573fSAndroid Build Coastguard Worker          descriptor.FieldDescriptor.LAST_RESERVED_FIELD_NUMBER -
130*1b3f573fSAndroid Build Coastguard Worker          descriptor.FieldDescriptor.FIRST_RESERVED_FIELD_NUMBER + 1)
131*1b3f573fSAndroid Build Coastguard Worker    field_proto.number = f_number
132*1b3f573fSAndroid Build Coastguard Worker    field_proto.label = descriptor_pb2.FieldDescriptorProto.LABEL_OPTIONAL
133*1b3f573fSAndroid Build Coastguard Worker    field_proto.type = f_type
134*1b3f573fSAndroid Build Coastguard Worker  return file_proto
135