{{! Copyright (c) Meta Platforms, Inc. and affiliates. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. }}{{! Included from CythonServices.pyx, this file defines the core adapter between the C++ service and the Python handler. The C++ classes defined in ServicesWrapper.h and ServicesWrapper.cpp call into this file using a generated header. It calls into the call_cy_.... functions, passing it a promise and the arguments. That function wraps the promise and arguments in Python types appropriately, then passes them into a coroutine defined in this same file. That coroutine then looks up the handler coroutine, which does the appropriate work and then returns a Python value. The coroutine accesses the C++ promise from the Python wrapper of that promise that was passed into the coroutine and sets the converted value on that promise. }} {{#program:services}} {{#service:supportedFunctions}} cdef api void call_cy_{{service:name}}_{{function:name}}( object self, Cpp2RequestContext* ctx, cFollyPromise[{{#function:return_type}}{{! }}{{> services/cython_return_type_cpp_type}}{{! }}{{/function:return_type}}]{{! }} cPromise{{#function:args}}, {{#field:type}}{{! }}{{^function:stack_arguments?}}{{> services/cython_field_type}}{{/function:stack_arguments?}}{{! }}{{#function:stack_arguments?}}{{> types/cython_cpp_type}}{{/function:stack_arguments?}}{{! }}{{/field:type}} {{field:py_name}}{{/function:args}} ) noexcept:{{! Gets called from a python Executor }} {{#function:return_type}} cdef {{> services/promise_name}} __promise = {{> services/promise_name}}._fbthrift_create(cmove(cPromise)) {{/function:return_type}} {{#function:args}} arg_{{field:py_name}} = {{#field:type}}{{! }}{{^function:stack_arguments?}}{{> services/cython_cpp_to_python}}{{/function:stack_arguments?}}{{! }}{{#function:stack_arguments?}}{{> types/cython_cpp_value_to_python}}{{/function:stack_arguments?}}{{! }}{{/field:type}} {{/function:args}} __context = RequestContext._fbthrift_create(ctx) __context_token = __THRIFT_REQUEST_CONTEXT.set(__context) asyncio.get_event_loop().create_task( {{service:name}}_{{function:name}}_coro( self, __promise{{#function:args}}, arg_{{field:py_name}}{{/function:args}} ) ) __THRIFT_REQUEST_CONTEXT.reset(__context_token) {{/service:supportedFunctions}} {{#service:lifecycleFunctions}} cdef api void call_cy_{{service:name}}_{{function:name}}( object self, cFollyPromise[{{#function:return_type}}{{! }}{{> services/cython_return_type_cpp_type}}{{! }}{{/function:return_type}}]{{! }} cPromise ) noexcept: {{#function:return_type}} cdef {{> services/promise_name}} __promise = {{> services/promise_name}}._fbthrift_create(cmove(cPromise)) {{/function:return_type}} asyncio.get_event_loop().create_task( {{service:name}}_{{function:name}}_coro( self, __promise ) ) {{/service:lifecycleFunctions}} {{#service:supportedFunctionsWithLifecycle}} async def {{service:name}}_{{function:name}}_coro( object self, {{#function:return_type}}{{> services/promise_name}}{{/function:return_type}}{{! }} promise{{#function:args}}, {{field:py_name}}{{/function:args}} ): try: result = {{#function:return_type}}{{^function:stream?}}await {{/function:stream?}}{{/function:return_type}}self.{{function:name}}({{#function:args}} {{field:py_name}}{{^last?}},{{/last?}}{{/function:args}}) {{#function:return_type}} {{#type:container?}} result = {{> types/cython_python_type}}(result) {{/type:container?}} {{#function:stream?}} {{#function:stream_has_first_response?}} result = await result item, result = result {{/function:stream_has_first_response?}} if not isinstance(result, (ServerStream, AsyncIterator)): result = await result if isinstance(result, AsyncIterator): {{#function:stream_elem_type}} result = ServerStream_{{> types/cython_cpp_type_ident}}._fbthrift_create(cmove(createAsyncIteratorFromPyIterator[{{ >types/cython_cpp_type}}](result, get_executor(), &getNextGenerator_{{service:name}}_{{function:name}}))) {{/function:stream_elem_type}} {{/function:stream?}} {{/function:return_type}} {{#function:exceptions}} {{#field:type}} except {{> types/cython_python_type}} as ex: promise.cPromise.setException(deref((<{{> types/cython_python_type}}> ex)._cpp_obj)) {{/field:type}} {{/function:exceptions}} except __ApplicationError as ex: # If the handler raised an ApplicationError convert it to a C++ one promise.cPromise.setException(cTApplicationException( ex.type.value, ex.message.encode('UTF-8') )) except Exception as ex: print( "Unexpected error in service handler {{service:name}}.{{function:name}}:", file=sys.stderr) traceback.print_exc() promise.cPromise.setException(cTApplicationException( cTApplicationExceptionType__UNKNOWN, repr(ex).encode('UTF-8') )) except asyncio.CancelledError as ex: print("Coroutine was cancelled in service handler {{service:name}}.{{function:name}}:", file=sys.stderr) traceback.print_exc() promise.cPromise.setException(cTApplicationException( cTApplicationExceptionType__UNKNOWN, (f'Application was cancelled on the server with message: {str(ex)}').encode('UTF-8') )) else: promise.cPromise.setValue({{#function:return_type}}{{> services/cython_return_value}}{{/function:return_type}}) {{/service:supportedFunctionsWithLifecycle}} {{/program:services}}