The Android Open Source Project | cf31fe9 | 2008-10-21 07:00:00 -0700 | [diff] [blame^] | 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 | self._CallMethod(srvc, method_descriptor, |
| 146 | rpc_controller, request, callback) |
| 147 | self.cls = cls |
| 148 | cls.CallMethod = _WrapCallMethod |
| 149 | cls.GetDescriptor = self._GetDescriptor |
| 150 | cls.GetRequestClass = self._GetRequestClass |
| 151 | cls.GetResponseClass = self._GetResponseClass |
| 152 | for method in self.descriptor.methods: |
| 153 | setattr(cls, method.name, self._GenerateNonImplementedMethod(method)) |
| 154 | |
| 155 | def _GetDescriptor(self): |
| 156 | """Retrieves the service descriptor. |
| 157 | |
| 158 | Returns: |
| 159 | The descriptor of the service (of type ServiceDescriptor). |
| 160 | """ |
| 161 | return self.descriptor |
| 162 | |
| 163 | def _CallMethod(self, srvc, method_descriptor, |
| 164 | rpc_controller, request, callback): |
| 165 | """Calls the method described by a given method descriptor. |
| 166 | |
| 167 | Args: |
| 168 | srvc: Instance of the service for which this method is called. |
| 169 | method_descriptor: Descriptor that represent the method to call. |
| 170 | rpc_controller: RPC controller to use for this method's execution. |
| 171 | request: Request protocol message. |
| 172 | callback: A callback to invoke after the method has completed. |
| 173 | """ |
| 174 | if method_descriptor.containing_service != self.descriptor: |
| 175 | raise RuntimeError( |
| 176 | 'CallMethod() given method descriptor for wrong service type.') |
| 177 | method = getattr(srvc, method_descriptor.name) |
| 178 | method(rpc_controller, request, callback) |
| 179 | |
| 180 | def _GetRequestClass(self, method_descriptor): |
| 181 | """Returns the class of the request protocol message. |
| 182 | |
| 183 | Args: |
| 184 | method_descriptor: Descriptor of the method for which to return the |
| 185 | request protocol message class. |
| 186 | |
| 187 | Returns: |
| 188 | A class that represents the input protocol message of the specified |
| 189 | method. |
| 190 | """ |
| 191 | if method_descriptor.containing_service != self.descriptor: |
| 192 | raise RuntimeError( |
| 193 | 'GetRequestClass() given method descriptor for wrong service type.') |
| 194 | return method_descriptor.input_type._concrete_class |
| 195 | |
| 196 | def _GetResponseClass(self, method_descriptor): |
| 197 | """Returns the class of the response protocol message. |
| 198 | |
| 199 | Args: |
| 200 | method_descriptor: Descriptor of the method for which to return the |
| 201 | response protocol message class. |
| 202 | |
| 203 | Returns: |
| 204 | A class that represents the output protocol message of the specified |
| 205 | method. |
| 206 | """ |
| 207 | if method_descriptor.containing_service != self.descriptor: |
| 208 | raise RuntimeError( |
| 209 | 'GetResponseClass() given method descriptor for wrong service type.') |
| 210 | return method_descriptor.output_type._concrete_class |
| 211 | |
| 212 | def _GenerateNonImplementedMethod(self, method): |
| 213 | """Generates and returns a method that can be set for a service methods. |
| 214 | |
| 215 | Args: |
| 216 | method: Descriptor of the service method for which a method is to be |
| 217 | generated. |
| 218 | |
| 219 | Returns: |
| 220 | A method that can be added to the service class. |
| 221 | """ |
| 222 | return lambda inst, rpc_controller, request, callback: ( |
| 223 | self._NonImplementedMethod(method.name, rpc_controller, callback)) |
| 224 | |
| 225 | def _NonImplementedMethod(self, method_name, rpc_controller, callback): |
| 226 | """The body of all methods in the generated service class. |
| 227 | |
| 228 | Args: |
| 229 | method_name: Name of the method being executed. |
| 230 | rpc_controller: RPC controller used to execute this method. |
| 231 | callback: A callback which will be invoked when the method finishes. |
| 232 | """ |
| 233 | rpc_controller.SetFailed('Method %s not implemented.' % method_name) |
| 234 | callback(None) |
| 235 | |
| 236 | |
| 237 | class _ServiceStubBuilder(object): |
| 238 | |
| 239 | """Constructs a protocol service stub class using a service descriptor. |
| 240 | |
| 241 | Given a service descriptor, this class constructs a suitable stub class. |
| 242 | A stub is just a type-safe wrapper around an RpcChannel which emulates a |
| 243 | local implementation of the service. |
| 244 | |
| 245 | One service stub builder instance constructs exactly one class. It means all |
| 246 | instances of that class share the same service stub builder. |
| 247 | """ |
| 248 | |
| 249 | def __init__(self, service_descriptor): |
| 250 | """Initializes an instance of the service stub class builder. |
| 251 | |
| 252 | Args: |
| 253 | service_descriptor: ServiceDescriptor to use when constructing the |
| 254 | stub class. |
| 255 | """ |
| 256 | self.descriptor = service_descriptor |
| 257 | |
| 258 | def BuildServiceStub(self, cls): |
| 259 | """Constructs the stub class. |
| 260 | |
| 261 | Args: |
| 262 | cls: The class that will be constructed. |
| 263 | """ |
| 264 | |
| 265 | def _ServiceStubInit(stub, rpc_channel): |
| 266 | stub.rpc_channel = rpc_channel |
| 267 | self.cls = cls |
| 268 | cls.__init__ = _ServiceStubInit |
| 269 | for method in self.descriptor.methods: |
| 270 | setattr(cls, method.name, self._GenerateStubMethod(method)) |
| 271 | |
| 272 | def _GenerateStubMethod(self, method): |
| 273 | return lambda inst, rpc_controller, request, callback: self._StubMethod( |
| 274 | inst, method, rpc_controller, request, callback) |
| 275 | |
| 276 | def _StubMethod(self, stub, method_descriptor, |
| 277 | rpc_controller, request, callback): |
| 278 | """The body of all service methods in the generated stub class. |
| 279 | |
| 280 | Args: |
| 281 | stub: Stub instance. |
| 282 | method_descriptor: Descriptor of the invoked method. |
| 283 | rpc_controller: Rpc controller to execute the method. |
| 284 | request: Request protocol message. |
| 285 | callback: A callback to execute when the method finishes. |
| 286 | """ |
| 287 | stub.rpc_channel.CallMethod( |
| 288 | method_descriptor, rpc_controller, request, |
| 289 | method_descriptor.output_type._concrete_class, callback) |