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