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