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