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 32classes from ServiceDescriptor objects at runtime. 33 34The GeneratedServiceType and GeneratedServiceStubType metaclasses are used to 35inject all useful functionality into the classes output by the protocol 36compiler at compile-time. 37""" 38 39__author__ = 'petar@google.com (Petar Petrov)' 40 41 42class 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 84class 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 114class _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 230class _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) 285