• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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