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