1# Protocol Buffers - Google's data interchange format 
    2# Copyright 2008 Google Inc.  All rights reserved. 
    3# 
    4# Use of this source code is governed by a BSD-style 
    5# license that can be found in the LICENSE file or at 
    6# https://developers.google.com/open-source/licenses/bsd 
    7 
    8"""Provides a factory class for generating dynamic messages. 
    9 
    10The easiest way to use this class is if you have access to the FileDescriptor 
    11protos containing the messages you want to create you can just do the following: 
    12 
    13message_classes = message_factory.GetMessages(iterable_of_file_descriptors) 
    14my_proto_instance = message_classes['some.proto.package.MessageName']() 
    15""" 
    16 
    17__author__ = 'matthewtoia@google.com (Matt Toia)' 
    18 
    19import warnings 
    20 
    21from google.protobuf import descriptor_pool 
    22from google.protobuf import message 
    23from google.protobuf.internal import api_implementation 
    24 
    25if api_implementation.Type() == 'python': 
    26  from google.protobuf.internal import python_message as message_impl 
    27else: 
    28  from google.protobuf.pyext import cpp_message as message_impl  # pylint: disable=g-import-not-at-top 
    29 
    30 
    31# The type of all Message classes. 
    32_GENERATED_PROTOCOL_MESSAGE_TYPE = message_impl.GeneratedProtocolMessageType 
    33 
    34 
    35def GetMessageClass(descriptor): 
    36  """Obtains a proto2 message class based on the passed in descriptor. 
    37 
    38  Passing a descriptor with a fully qualified name matching a previous 
    39  invocation will cause the same class to be returned. 
    40 
    41  Args: 
    42    descriptor: The descriptor to build from. 
    43 
    44  Returns: 
    45    A class describing the passed in descriptor. 
    46  """ 
    47  concrete_class = getattr(descriptor, '_concrete_class', None) 
    48  if concrete_class: 
    49    return concrete_class 
    50  return _InternalCreateMessageClass(descriptor) 
    51 
    52 
    53def GetMessageClassesForFiles(files, pool): 
    54  """Gets all the messages from specified files. 
    55 
    56  This will find and resolve dependencies, failing if the descriptor 
    57  pool cannot satisfy them. 
    58 
    59  This will not return the classes for nested types within those classes, for 
    60  those, use GetMessageClass() on the nested types within their containing 
    61  messages. 
    62 
    63  For example, for the message: 
    64 
    65  message NestedTypeMessage { 
    66    message NestedType { 
    67      string data = 1; 
    68    } 
    69    NestedType nested = 1; 
    70  } 
    71 
    72  NestedTypeMessage will be in the result, but not 
    73  NestedTypeMessage.NestedType. 
    74 
    75  Args: 
    76    files: The file names to extract messages from. 
    77    pool: The descriptor pool to find the files including the dependent files. 
    78 
    79  Returns: 
    80    A dictionary mapping proto names to the message classes. 
    81  """ 
    82  result = {} 
    83  for file_name in files: 
    84    file_desc = pool.FindFileByName(file_name) 
    85    for desc in file_desc.message_types_by_name.values(): 
    86      result[desc.full_name] = GetMessageClass(desc) 
    87 
    88    # While the extension FieldDescriptors are created by the descriptor pool, 
    89    # the python classes created in the factory need them to be registered 
    90    # explicitly, which is done below. 
    91    # 
    92    # The call to RegisterExtension will specifically check if the 
    93    # extension was already registered on the object and either 
    94    # ignore the registration if the original was the same, or raise 
    95    # an error if they were different. 
    96 
    97    for extension in file_desc.extensions_by_name.values(): 
    98      _ = GetMessageClass(extension.containing_type) 
    99      if api_implementation.Type() != 'python': 
    100        # TODO: Remove this check here. Duplicate extension 
    101        # register check should be in descriptor_pool. 
    102        if extension is not pool.FindExtensionByNumber( 
    103            extension.containing_type, extension.number 
    104        ): 
    105          raise ValueError('Double registration of Extensions') 
    106      # Recursively load protos for extension field, in order to be able to 
    107      # fully represent the extension. This matches the behavior for regular 
    108      # fields too. 
    109      if extension.message_type: 
    110        GetMessageClass(extension.message_type) 
    111  return result 
    112 
    113 
    114def _InternalCreateMessageClass(descriptor): 
    115  """Builds a proto2 message class based on the passed in descriptor. 
    116 
    117  Args: 
    118    descriptor: The descriptor to build from. 
    119 
    120  Returns: 
    121    A class describing the passed in descriptor. 
    122  """ 
    123  descriptor_name = descriptor.name 
    124  result_class = _GENERATED_PROTOCOL_MESSAGE_TYPE( 
    125      descriptor_name, 
    126      (message.Message,), 
    127      { 
    128          'DESCRIPTOR': descriptor, 
    129          # If module not set, it wrongly points to message_factory module. 
    130          '__module__': None, 
    131      }, 
    132  ) 
    133  for field in descriptor.fields: 
    134    if field.message_type: 
    135      GetMessageClass(field.message_type) 
    136 
    137  for extension in result_class.DESCRIPTOR.extensions: 
    138    extended_class = GetMessageClass(extension.containing_type) 
    139    if api_implementation.Type() != 'python': 
    140      # TODO: Remove this check here. Duplicate extension 
    141      # register check should be in descriptor_pool. 
    142      pool = extension.containing_type.file.pool 
    143      if extension is not pool.FindExtensionByNumber( 
    144          extension.containing_type, extension.number 
    145      ): 
    146        raise ValueError('Double registration of Extensions') 
    147    if extension.message_type: 
    148      GetMessageClass(extension.message_type) 
    149  return result_class 
    150 
    151 
    152# Deprecated. Please use GetMessageClass() or GetMessageClassesForFiles() 
    153# method above instead. 
    154class MessageFactory(object): 
    155  """Factory for creating Proto2 messages from descriptors in a pool.""" 
    156 
    157  def __init__(self, pool=None): 
    158    """Initializes a new factory.""" 
    159    self.pool = pool or descriptor_pool.DescriptorPool() 
    160 
    161 
    162def GetMessages(file_protos, pool=None): 
    163  """Builds a dictionary of all the messages available in a set of files. 
    164 
    165  Args: 
    166    file_protos: Iterable of FileDescriptorProto to build messages out of. 
    167    pool: The descriptor pool to add the file protos. 
    168 
    169  Returns: 
    170    A dictionary mapping proto names to the message classes. This will include 
    171    any dependent messages as well as any messages defined in the same file as 
    172    a specified message. 
    173  """ 
    174  # The cpp implementation of the protocol buffer library requires to add the 
    175  # message in topological order of the dependency graph. 
    176  des_pool = pool or descriptor_pool.DescriptorPool() 
    177  file_by_name = {file_proto.name: file_proto for file_proto in file_protos} 
    178 
    179  def _AddFile(file_proto): 
    180    for dependency in file_proto.dependency: 
    181      if dependency in file_by_name: 
    182        # Remove from elements to be visited, in order to cut cycles. 
    183        _AddFile(file_by_name.pop(dependency)) 
    184    des_pool.Add(file_proto) 
    185 
    186  while file_by_name: 
    187    _AddFile(file_by_name.popitem()[1]) 
    188  return GetMessageClassesForFiles( 
    189      [file_proto.name for file_proto in file_protos], des_pool 
    190  )