1a3b2a6da25a76f17c73d31def3952feb0fd2296eJeff Davidson# Protocol Buffers - Google's data interchange format
2a3b2a6da25a76f17c73d31def3952feb0fd2296eJeff Davidson# Copyright 2008 Google Inc.  All rights reserved.
3afb4b72037e3f13db208590fc782c4bc8e27f862Jeff Davidson# https://developers.google.com/protocol-buffers/
4a3b2a6da25a76f17c73d31def3952feb0fd2296eJeff Davidson#
5a3b2a6da25a76f17c73d31def3952feb0fd2296eJeff Davidson# Redistribution and use in source and binary forms, with or without
6a3b2a6da25a76f17c73d31def3952feb0fd2296eJeff Davidson# modification, are permitted provided that the following conditions are
7a3b2a6da25a76f17c73d31def3952feb0fd2296eJeff Davidson# met:
8a3b2a6da25a76f17c73d31def3952feb0fd2296eJeff Davidson#
9a3b2a6da25a76f17c73d31def3952feb0fd2296eJeff Davidson#     * Redistributions of source code must retain the above copyright
10a3b2a6da25a76f17c73d31def3952feb0fd2296eJeff Davidson# notice, this list of conditions and the following disclaimer.
11a3b2a6da25a76f17c73d31def3952feb0fd2296eJeff Davidson#     * Redistributions in binary form must reproduce the above
12a3b2a6da25a76f17c73d31def3952feb0fd2296eJeff Davidson# copyright notice, this list of conditions and the following disclaimer
13a3b2a6da25a76f17c73d31def3952feb0fd2296eJeff Davidson# in the documentation and/or other materials provided with the
14a3b2a6da25a76f17c73d31def3952feb0fd2296eJeff Davidson# distribution.
15a3b2a6da25a76f17c73d31def3952feb0fd2296eJeff Davidson#     * Neither the name of Google Inc. nor the names of its
16a3b2a6da25a76f17c73d31def3952feb0fd2296eJeff Davidson# contributors may be used to endorse or promote products derived from
17a3b2a6da25a76f17c73d31def3952feb0fd2296eJeff Davidson# this software without specific prior written permission.
18a3b2a6da25a76f17c73d31def3952feb0fd2296eJeff Davidson#
19a3b2a6da25a76f17c73d31def3952feb0fd2296eJeff Davidson# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20a3b2a6da25a76f17c73d31def3952feb0fd2296eJeff Davidson# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21a3b2a6da25a76f17c73d31def3952feb0fd2296eJeff Davidson# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22a3b2a6da25a76f17c73d31def3952feb0fd2296eJeff Davidson# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
23a3b2a6da25a76f17c73d31def3952feb0fd2296eJeff Davidson# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24a3b2a6da25a76f17c73d31def3952feb0fd2296eJeff Davidson# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25a3b2a6da25a76f17c73d31def3952feb0fd2296eJeff Davidson# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26a3b2a6da25a76f17c73d31def3952feb0fd2296eJeff Davidson# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27a3b2a6da25a76f17c73d31def3952feb0fd2296eJeff Davidson# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28a3b2a6da25a76f17c73d31def3952feb0fd2296eJeff Davidson# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29a3b2a6da25a76f17c73d31def3952feb0fd2296eJeff Davidson# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30a3b2a6da25a76f17c73d31def3952feb0fd2296eJeff Davidson
31a3b2a6da25a76f17c73d31def3952feb0fd2296eJeff Davidson#PY25 compatible for GAE.
32a3b2a6da25a76f17c73d31def3952feb0fd2296eJeff Davidson#
33a3b2a6da25a76f17c73d31def3952feb0fd2296eJeff Davidson# Copyright 2012 Google Inc. All Rights Reserved.
34a3b2a6da25a76f17c73d31def3952feb0fd2296eJeff Davidson
35a3b2a6da25a76f17c73d31def3952feb0fd2296eJeff Davidson"""Provides a factory class for generating dynamic messages.
36a3b2a6da25a76f17c73d31def3952feb0fd2296eJeff Davidson
37a3b2a6da25a76f17c73d31def3952feb0fd2296eJeff DavidsonThe easiest way to use this class is if you have access to the FileDescriptor
38a3b2a6da25a76f17c73d31def3952feb0fd2296eJeff Davidsonprotos containing the messages you want to create you can just do the following:
39a3b2a6da25a76f17c73d31def3952feb0fd2296eJeff Davidson
40a3b2a6da25a76f17c73d31def3952feb0fd2296eJeff Davidsonmessage_classes = message_factory.GetMessages(iterable_of_file_descriptors)
41a3b2a6da25a76f17c73d31def3952feb0fd2296eJeff Davidsonmy_proto_instance = message_classes['some.proto.package.MessageName']()
42a3b2a6da25a76f17c73d31def3952feb0fd2296eJeff Davidson"""
43a3b2a6da25a76f17c73d31def3952feb0fd2296eJeff Davidson
44a3b2a6da25a76f17c73d31def3952feb0fd2296eJeff Davidson__author__ = 'matthewtoia@google.com (Matt Toia)'
45a3b2a6da25a76f17c73d31def3952feb0fd2296eJeff Davidson
46a3b2a6da25a76f17c73d31def3952feb0fd2296eJeff Davidsonimport sys  ##PY25
47a3b2a6da25a76f17c73d31def3952feb0fd2296eJeff Davidsonfrom google.protobuf import descriptor_database
48a3b2a6da25a76f17c73d31def3952feb0fd2296eJeff Davidsonfrom google.protobuf import descriptor_pool
49a3b2a6da25a76f17c73d31def3952feb0fd2296eJeff Davidsonfrom google.protobuf import message
50a3b2a6da25a76f17c73d31def3952feb0fd2296eJeff Davidsonfrom google.protobuf import reflection
51a3b2a6da25a76f17c73d31def3952feb0fd2296eJeff Davidson
52a3b2a6da25a76f17c73d31def3952feb0fd2296eJeff Davidson
53a3b2a6da25a76f17c73d31def3952feb0fd2296eJeff Davidsonclass MessageFactory(object):
54a3b2a6da25a76f17c73d31def3952feb0fd2296eJeff Davidson  """Factory for creating Proto2 messages from descriptors in a pool."""
55a3b2a6da25a76f17c73d31def3952feb0fd2296eJeff Davidson
56a3b2a6da25a76f17c73d31def3952feb0fd2296eJeff Davidson  def __init__(self, pool=None):
57a3b2a6da25a76f17c73d31def3952feb0fd2296eJeff Davidson    """Initializes a new factory."""
58a3b2a6da25a76f17c73d31def3952feb0fd2296eJeff Davidson    self.pool = (pool or descriptor_pool.DescriptorPool(
59a3b2a6da25a76f17c73d31def3952feb0fd2296eJeff Davidson        descriptor_database.DescriptorDatabase()))
60a3b2a6da25a76f17c73d31def3952feb0fd2296eJeff Davidson
61a3b2a6da25a76f17c73d31def3952feb0fd2296eJeff Davidson    # local cache of all classes built from protobuf descriptors
62a3b2a6da25a76f17c73d31def3952feb0fd2296eJeff Davidson    self._classes = {}
63a3b2a6da25a76f17c73d31def3952feb0fd2296eJeff Davidson
64a3b2a6da25a76f17c73d31def3952feb0fd2296eJeff Davidson  def GetPrototype(self, descriptor):
65a3b2a6da25a76f17c73d31def3952feb0fd2296eJeff Davidson    """Builds a proto2 message class based on the passed in descriptor.
66a3b2a6da25a76f17c73d31def3952feb0fd2296eJeff Davidson
67a3b2a6da25a76f17c73d31def3952feb0fd2296eJeff Davidson    Passing a descriptor with a fully qualified name matching a previous
68a3b2a6da25a76f17c73d31def3952feb0fd2296eJeff Davidson    invocation will cause the same class to be returned.
69a3b2a6da25a76f17c73d31def3952feb0fd2296eJeff Davidson
70a3b2a6da25a76f17c73d31def3952feb0fd2296eJeff Davidson    Args:
71a3b2a6da25a76f17c73d31def3952feb0fd2296eJeff Davidson      descriptor: The descriptor to build from.
72a3b2a6da25a76f17c73d31def3952feb0fd2296eJeff Davidson
73a3b2a6da25a76f17c73d31def3952feb0fd2296eJeff Davidson    Returns:
74a3b2a6da25a76f17c73d31def3952feb0fd2296eJeff Davidson      A class describing the passed in descriptor.
75a3b2a6da25a76f17c73d31def3952feb0fd2296eJeff Davidson    """
76a3b2a6da25a76f17c73d31def3952feb0fd2296eJeff Davidson    if descriptor.full_name not in self._classes:
77a3b2a6da25a76f17c73d31def3952feb0fd2296eJeff Davidson      descriptor_name = descriptor.name
78a3b2a6da25a76f17c73d31def3952feb0fd2296eJeff Davidson      if sys.version_info[0] < 3:  ##PY25
79a3b2a6da25a76f17c73d31def3952feb0fd2296eJeff Davidson##!PY25      if str is bytes:  # PY2
80a3b2a6da25a76f17c73d31def3952feb0fd2296eJeff Davidson        descriptor_name = descriptor.name.encode('ascii', 'ignore')
81a3b2a6da25a76f17c73d31def3952feb0fd2296eJeff Davidson      result_class = reflection.GeneratedProtocolMessageType(
82a3b2a6da25a76f17c73d31def3952feb0fd2296eJeff Davidson          descriptor_name,
83a3b2a6da25a76f17c73d31def3952feb0fd2296eJeff Davidson          (message.Message,),
84a3b2a6da25a76f17c73d31def3952feb0fd2296eJeff Davidson          {'DESCRIPTOR': descriptor, '__module__': None})
85a3b2a6da25a76f17c73d31def3952feb0fd2296eJeff Davidson          # If module not set, it wrongly points to the reflection.py module.
86a3b2a6da25a76f17c73d31def3952feb0fd2296eJeff Davidson      self._classes[descriptor.full_name] = result_class
87a3b2a6da25a76f17c73d31def3952feb0fd2296eJeff Davidson      for field in descriptor.fields:
88a3b2a6da25a76f17c73d31def3952feb0fd2296eJeff Davidson        if field.message_type:
89a3b2a6da25a76f17c73d31def3952feb0fd2296eJeff Davidson          self.GetPrototype(field.message_type)
90a3b2a6da25a76f17c73d31def3952feb0fd2296eJeff Davidson      for extension in result_class.DESCRIPTOR.extensions:
91a3b2a6da25a76f17c73d31def3952feb0fd2296eJeff Davidson        if extension.containing_type.full_name not in self._classes:
92a3b2a6da25a76f17c73d31def3952feb0fd2296eJeff Davidson          self.GetPrototype(extension.containing_type)
93a3b2a6da25a76f17c73d31def3952feb0fd2296eJeff Davidson        extended_class = self._classes[extension.containing_type.full_name]
94a3b2a6da25a76f17c73d31def3952feb0fd2296eJeff Davidson        extended_class.RegisterExtension(extension)
95a3b2a6da25a76f17c73d31def3952feb0fd2296eJeff Davidson    return self._classes[descriptor.full_name]
96a3b2a6da25a76f17c73d31def3952feb0fd2296eJeff Davidson
97a3b2a6da25a76f17c73d31def3952feb0fd2296eJeff Davidson  def GetMessages(self, files):
98a3b2a6da25a76f17c73d31def3952feb0fd2296eJeff Davidson    """Gets all the messages from a specified file.
99a3b2a6da25a76f17c73d31def3952feb0fd2296eJeff Davidson
100a3b2a6da25a76f17c73d31def3952feb0fd2296eJeff Davidson    This will find and resolve dependencies, failing if the descriptor
101a3b2a6da25a76f17c73d31def3952feb0fd2296eJeff Davidson    pool cannot satisfy them.
102a3b2a6da25a76f17c73d31def3952feb0fd2296eJeff Davidson
103a3b2a6da25a76f17c73d31def3952feb0fd2296eJeff Davidson    Args:
104a3b2a6da25a76f17c73d31def3952feb0fd2296eJeff Davidson      files: The file names to extract messages from.
105a3b2a6da25a76f17c73d31def3952feb0fd2296eJeff Davidson
106a3b2a6da25a76f17c73d31def3952feb0fd2296eJeff Davidson    Returns:
107a3b2a6da25a76f17c73d31def3952feb0fd2296eJeff Davidson      A dictionary mapping proto names to the message classes. This will include
108a3b2a6da25a76f17c73d31def3952feb0fd2296eJeff Davidson      any dependent messages as well as any messages defined in the same file as
109a3b2a6da25a76f17c73d31def3952feb0fd2296eJeff Davidson      a specified message.
110a3b2a6da25a76f17c73d31def3952feb0fd2296eJeff Davidson    """
111a3b2a6da25a76f17c73d31def3952feb0fd2296eJeff Davidson    result = {}
112a3b2a6da25a76f17c73d31def3952feb0fd2296eJeff Davidson    for file_name in files:
113a3b2a6da25a76f17c73d31def3952feb0fd2296eJeff Davidson      file_desc = self.pool.FindFileByName(file_name)
114a3b2a6da25a76f17c73d31def3952feb0fd2296eJeff Davidson      for name, msg in file_desc.message_types_by_name.iteritems():
115a3b2a6da25a76f17c73d31def3952feb0fd2296eJeff Davidson        if file_desc.package:
116a3b2a6da25a76f17c73d31def3952feb0fd2296eJeff Davidson          full_name = '.'.join([file_desc.package, name])
117a3b2a6da25a76f17c73d31def3952feb0fd2296eJeff Davidson        else:
118a3b2a6da25a76f17c73d31def3952feb0fd2296eJeff Davidson          full_name = msg.name
119a3b2a6da25a76f17c73d31def3952feb0fd2296eJeff Davidson        result[full_name] = self.GetPrototype(
120a3b2a6da25a76f17c73d31def3952feb0fd2296eJeff Davidson            self.pool.FindMessageTypeByName(full_name))
121a3b2a6da25a76f17c73d31def3952feb0fd2296eJeff Davidson
122a3b2a6da25a76f17c73d31def3952feb0fd2296eJeff Davidson      # While the extension FieldDescriptors are created by the descriptor pool,
123a3b2a6da25a76f17c73d31def3952feb0fd2296eJeff Davidson      # the python classes created in the factory need them to be registered
124a3b2a6da25a76f17c73d31def3952feb0fd2296eJeff Davidson      # explicitly, which is done below.
125a3b2a6da25a76f17c73d31def3952feb0fd2296eJeff Davidson      #
126a3b2a6da25a76f17c73d31def3952feb0fd2296eJeff Davidson      # The call to RegisterExtension will specifically check if the
127a3b2a6da25a76f17c73d31def3952feb0fd2296eJeff Davidson      # extension was already registered on the object and either
128a3b2a6da25a76f17c73d31def3952feb0fd2296eJeff Davidson      # ignore the registration if the original was the same, or raise
129a3b2a6da25a76f17c73d31def3952feb0fd2296eJeff Davidson      # an error if they were different.
130a3b2a6da25a76f17c73d31def3952feb0fd2296eJeff Davidson
131a3b2a6da25a76f17c73d31def3952feb0fd2296eJeff Davidson      for name, extension in file_desc.extensions_by_name.iteritems():
132a3b2a6da25a76f17c73d31def3952feb0fd2296eJeff Davidson        if extension.containing_type.full_name not in self._classes:
133a3b2a6da25a76f17c73d31def3952feb0fd2296eJeff Davidson          self.GetPrototype(extension.containing_type)
134a3b2a6da25a76f17c73d31def3952feb0fd2296eJeff Davidson        extended_class = self._classes[extension.containing_type.full_name]
135a3b2a6da25a76f17c73d31def3952feb0fd2296eJeff Davidson        extended_class.RegisterExtension(extension)
136a3b2a6da25a76f17c73d31def3952feb0fd2296eJeff Davidson    return result
137a3b2a6da25a76f17c73d31def3952feb0fd2296eJeff Davidson
138a3b2a6da25a76f17c73d31def3952feb0fd2296eJeff Davidson
139a3b2a6da25a76f17c73d31def3952feb0fd2296eJeff Davidson_FACTORY = MessageFactory()
140a3b2a6da25a76f17c73d31def3952feb0fd2296eJeff Davidson
141a3b2a6da25a76f17c73d31def3952feb0fd2296eJeff Davidson
142a3b2a6da25a76f17c73d31def3952feb0fd2296eJeff Davidsondef GetMessages(file_protos):
143a3b2a6da25a76f17c73d31def3952feb0fd2296eJeff Davidson  """Builds a dictionary of all the messages available in a set of files.
144a3b2a6da25a76f17c73d31def3952feb0fd2296eJeff Davidson
145a3b2a6da25a76f17c73d31def3952feb0fd2296eJeff Davidson  Args:
146a3b2a6da25a76f17c73d31def3952feb0fd2296eJeff Davidson    file_protos: A sequence of file protos to build messages out of.
147a3b2a6da25a76f17c73d31def3952feb0fd2296eJeff Davidson
148a3b2a6da25a76f17c73d31def3952feb0fd2296eJeff Davidson  Returns:
149a3b2a6da25a76f17c73d31def3952feb0fd2296eJeff Davidson    A dictionary mapping proto names to the message classes. This will include
150a3b2a6da25a76f17c73d31def3952feb0fd2296eJeff Davidson    any dependent messages as well as any messages defined in the same file as
151a3b2a6da25a76f17c73d31def3952feb0fd2296eJeff Davidson    a specified message.
152a3b2a6da25a76f17c73d31def3952feb0fd2296eJeff Davidson  """
153a3b2a6da25a76f17c73d31def3952feb0fd2296eJeff Davidson  for file_proto in file_protos:
154a3b2a6da25a76f17c73d31def3952feb0fd2296eJeff Davidson    _FACTORY.pool.Add(file_proto)
155a3b2a6da25a76f17c73d31def3952feb0fd2296eJeff Davidson  return _FACTORY.GetMessages([file_proto.name for file_proto in file_protos])
156