tizen 2.3.1 release
[external/protobuf.git] / python / google / protobuf / service_reflection.py
1 # Protocol Buffers - Google's data interchange format
2 # Copyright 2008 Google Inc.  All rights reserved.
3 # https://developers.google.com/protocol-buffers/
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)