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 """Contains metaclasses used to create protocol service and service stub | |
32 classes from ServiceDescriptor objects at runtime. | |
33 | |
34 The GeneratedServiceType and GeneratedServiceStubType metaclasses are used to | |
35 inject all useful functionality into the classes output by the protocol | |
36 compiler at compile-time. | |
37 """ | |
38 | |
39 __author__ = 'petar@google.com (Petar Petrov)' | |
40 | |
41 | |
42 class GeneratedServiceType(type): | |
43 | |
44 """Metaclass for service classes created at runtime from ServiceDescriptors. | |
45 | |
46 Implementations for all methods described in the Service class are added here | |
47 by this class. We also create properties to allow getting/setting all fields | |
48 in the protocol message. | |
49 | |
50 The protocol compiler currently uses this metaclass to create protocol service | |
51 classes at runtime. Clients can also manually create their own classes at | |
52 runtime, as in this example: | |
53 | |
54 mydescriptor = ServiceDescriptor(.....) | |
55 class MyProtoService(service.Service): | |
56 __metaclass__ = GeneratedServiceType | |
57 DESCRIPTOR = mydescriptor | |
58 myservice_instance = MyProtoService() | |
59 ... | |
60 """ | |
61 | |
62 _DESCRIPTOR_KEY = 'DESCRIPTOR' | |
63 | |
64 def __init__(cls, name, bases, dictionary): | |
65 """Creates a message service class. | |
66 | |
67 Args: | |
68 name: Name of the class (ignored, but required by the metaclass | |
69 protocol). | |
70 bases: Base classes of the class being constructed. | |
71 dictionary: The class dictionary of the class being constructed. | |
72 dictionary[_DESCRIPTOR_KEY] must contain a ServiceDescriptor object | |
73 describing this protocol service type. | |
74 """ | |
75 # Don't do anything if this class doesn't have a descriptor. This happens | |
76 # when a service class is subclassed. | |
77 if GeneratedServiceType._DESCRIPTOR_KEY not in dictionary: | |
78 return | |
79 descriptor = dictionary[GeneratedServiceType._DESCRIPTOR_KEY] | |
80 service_builder = _ServiceBuilder(descriptor) | |
81 service_builder.BuildService(cls) | |
82 | |
83 | |
84 class GeneratedServiceStubType(GeneratedServiceType): | |
85 | |
86 """Metaclass for service stubs created at runtime from ServiceDescriptors. | |
87 | |
88 This class has similar responsibilities as GeneratedServiceType, except that | |
89 it creates the service stub classes. | |
90 """ | |
91 | |
92 _DESCRIPTOR_KEY = 'DESCRIPTOR' | |
93 | |
94 def __init__(cls, name, bases, dictionary): | |
95 """Creates a message service stub class. | |
96 | |
97 Args: | |
98 name: Name of the class (ignored, here). | |
99 bases: Base classes of the class being constructed. | |
100 dictionary: The class dictionary of the class being constructed. | |
101 dictionary[_DESCRIPTOR_KEY] must contain a ServiceDescriptor object | |
102 describing this protocol service type. | |
103 """ | |
104 super(GeneratedServiceStubType, cls).__init__(name, bases, dictionary) | |
105 # Don't do anything if this class doesn't have a descriptor. This happens | |
106 # when a service stub is subclassed. | |
107 if GeneratedServiceStubType._DESCRIPTOR_KEY not in dictionary: | |
108 return | |
109 descriptor = dictionary[GeneratedServiceStubType._DESCRIPTOR_KEY] | |
110 service_stub_builder = _ServiceStubBuilder(descriptor) | |
111 service_stub_builder.BuildServiceStub(cls) | |
112 | |
113 | |
114 class _ServiceBuilder(object): | |
115 | |
116 """This class constructs a protocol service class using a service descriptor. | |
117 | |
118 Given a service descriptor, this class constructs a class that represents | |
119 the specified service descriptor. One service builder instance constructs | |
120 exactly one service class. That means all instances of that class share the | |
121 same builder. | |
122 """ | |
123 | |
124 def __init__(self, service_descriptor): | |
125 """Initializes an instance of the service class builder. | |
126 | |
127 Args: | |
128 service_descriptor: ServiceDescriptor to use when constructing the | |
129 service class. | |
130 """ | |
131 self.descriptor = service_descriptor | |
132 | |
133 def BuildService(self, cls): | |
134 """Constructs the service class. | |
135 | |
136 Args: | |
137 cls: The class that will be constructed. | |
138 """ | |
139 | |
140 # CallMethod needs to operate with an instance of the Service class. This | |
141 # internal wrapper function exists only to be able to pass the service | |
142 # instance to the method that does the real CallMethod work. | |
143 def _WrapCallMethod(srvc, method_descriptor, | |
144 rpc_controller, request, callback): | |
145 return self._CallMethod(srvc, method_descriptor, | |
146 rpc_controller, request, callback) | |
147 self.cls = cls | |
148 cls.CallMethod = _WrapCallMethod | |
149 cls.GetDescriptor = staticmethod(lambda: self.descriptor) | |
150 cls.GetDescriptor.__doc__ = "Returns the service descriptor." | |
151 cls.GetRequestClass = self._GetRequestClass | |
152 cls.GetResponseClass = self._GetResponseClass | |
153 for method in self.descriptor.methods: | |
154 setattr(cls, method.name, self._GenerateNonImplementedMethod(method)) | |
155 | |
156 def _CallMethod(self, srvc, method_descriptor, | |
157 rpc_controller, request, callback): | |
158 """Calls the method described by a given method descriptor. | |
159 | |
160 Args: | |
161 srvc: Instance of the service for which this method is called. | |
162 method_descriptor: Descriptor that represent the method to call. | |
163 rpc_controller: RPC controller to use for this method's execution. | |
164 request: Request protocol message. | |
165 callback: A callback to invoke after the method has completed. | |
166 """ | |
167 if method_descriptor.containing_service != self.descriptor: | |
168 raise RuntimeError( | |
169 'CallMethod() given method descriptor for wrong service type.') | |
170 method = getattr(srvc, method_descriptor.name) | |
171 return method(rpc_controller, request, callback) | |
172 | |
173 def _GetRequestClass(self, method_descriptor): | |
174 """Returns the class of the request protocol message. | |
175 | |
176 Args: | |
177 method_descriptor: Descriptor of the method for which to return the | |
178 request protocol message class. | |
179 | |
180 Returns: | |
181 A class that represents the input protocol message of the specified | |
182 method. | |
183 """ | |
184 if method_descriptor.containing_service != self.descriptor: | |
185 raise RuntimeError( | |
186 'GetRequestClass() given method descriptor for wrong service type.') | |
187 return method_descriptor.input_type._concrete_class | |
188 | |
189 def _GetResponseClass(self, method_descriptor): | |
190 """Returns the class of the response protocol message. | |
191 | |
192 Args: | |
193 method_descriptor: Descriptor of the method for which to return the | |
194 response protocol message class. | |
195 | |
196 Returns: | |
197 A class that represents the output protocol message of the specified | |
198 method. | |
199 """ | |
200 if method_descriptor.containing_service != self.descriptor: | |
201 raise RuntimeError( | |
202 'GetResponseClass() given method descriptor for wrong service type.') | |
203 return method_descriptor.output_type._concrete_class | |
204 | |
205 def _GenerateNonImplementedMethod(self, method): | |
206 """Generates and returns a method that can be set for a service methods. | |
207 | |
208 Args: | |
209 method: Descriptor of the service method for which a method is to be | |
210 generated. | |
211 | |
212 Returns: | |
213 A method that can be added to the service class. | |
214 """ | |
215 return lambda inst, rpc_controller, request, callback: ( | |
216 self._NonImplementedMethod(method.name, rpc_controller, callback)) | |
217 | |
218 def _NonImplementedMethod(self, method_name, rpc_controller, callback): | |
219 """The body of all methods in the generated service class. | |
220 | |
221 Args: | |
222 method_name: Name of the method being executed. | |
223 rpc_controller: RPC controller used to execute this method. | |
224 callback: A callback which will be invoked when the method finishes. | |
225 """ | |
226 rpc_controller.SetFailed('Method %s not implemented.' % method_name) | |
227 callback(None) | |
228 | |
229 | |
230 class _ServiceStubBuilder(object): | |
231 | |
232 """Constructs a protocol service stub class using a service descriptor. | |
233 | |
234 Given a service descriptor, this class constructs a suitable stub class. | |
235 A stub is just a type-safe wrapper around an RpcChannel which emulates a | |
236 local implementation of the service. | |
237 | |
238 One service stub builder instance constructs exactly one class. It means all | |
239 instances of that class share the same service stub builder. | |
240 """ | |
241 | |
242 def __init__(self, service_descriptor): | |
243 """Initializes an instance of the service stub class builder. | |
244 | |
245 Args: | |
246 service_descriptor: ServiceDescriptor to use when constructing the | |
247 stub class. | |
248 """ | |
249 self.descriptor = service_descriptor | |
250 | |
251 def BuildServiceStub(self, cls): | |
252 """Constructs the stub class. | |
253 | |
254 Args: | |
255 cls: The class that will be constructed. | |
256 """ | |
257 | |
258 def _ServiceStubInit(stub, rpc_channel): | |
259 stub.rpc_channel = rpc_channel | |
260 self.cls = cls | |
261 cls.__init__ = _ServiceStubInit | |
262 for method in self.descriptor.methods: | |
263 setattr(cls, method.name, self._GenerateStubMethod(method)) | |
264 | |
265 def _GenerateStubMethod(self, method): | |
266 return (lambda inst, rpc_controller, request, callback=None: | |
267 self._StubMethod(inst, method, rpc_controller, request, callback)) | |
268 | |
269 def _StubMethod(self, stub, method_descriptor, | |
270 rpc_controller, request, callback): | |
271 """The body of all service methods in the generated stub class. | |
272 | |
273 Args: | |
274 stub: Stub instance. | |
275 method_descriptor: Descriptor of the invoked method. | |
276 rpc_controller: Rpc controller to execute the method. | |
277 request: Request protocol message. | |
278 callback: A callback to execute when the method finishes. | |
279 Returns: | |
280 Response message (in case of blocking call). | |
281 """ | |
282 return stub.rpc_channel.CallMethod( | |
283 method_descriptor, rpc_controller, request, | |
284 method_descriptor.output_type._concrete_class, callback) | |
OLD | NEW |