Async Function with API

Question:

TypeError: The view function did not return a valid response. The return type must be a string, dict, tuple, Response instance, or WSGI callable, but it was a coroutine.

I need to use asynchronous view function in Flask, and the interface function predict should return the result of calling the asynchronous function model.transform(data_set, ‘prediction’). However, I have tried various methods including:

  1. defining an asynchronous view function using async def and using await to wait for the results of asynchronous operations;
  2. using the async_helpers module;
  3. using Flask and asyncio to implement an asynchronous function interface. I tried using Flask’s Blueprint and the run_in_executor method in asyncio to define the asynchronous interface.
    However, all these methods returned an error that the response is not a valid response but a coroutine object. Do you have any suggestions to solve this problem? My project depends on Flask-2.0.2 and Werkzeug-2.0.3.
@bp.route("/predict", methods=["POST"])
@pre.catch({
    "userId": Rule(callback=ParamsCallback.convert_to_int, dest="user_id"),
    "modelId": Rule(type=str, dest="model_id"),
    "database": Rule(type=str, dest="database"),
    "table": Rule(type=str, dest="table")
})
# @async_helpers.async_suppress
@asyncio.coroutine
async def predictaa(params):
    # result = Result.get_model(user_id=params["user_id"], model_id=params["model_id"])
    result = Result.get_model(**params)
    model_id_value = params['model_id']
    if result is None:
        raise OperatorException("error", f"模型{model_id_value}不存在!")
    parameters = json.loads(result.parameterJson)  # 从请求体中获取要获取的数据。
    model_class = parameters.pop("model_class")
    parameters["user_id"] = params['user_id']
    parameters["process_id"] = result.targetId
    parameters["output_port_id"] = 27
    # model = eval("%s(**parameters)" % model_class) 

    data = json.loads(request.data)
    features = data.get("features")
    model_type = data.get("model_type")
    model_params = data.get("model_params")
    data_set = data.get("data")

    if model is None:
        return jsonify({'error': 'Model not found'})
    try:
    #     loop = asyncio.get_event_loop()
    #     result = await loop.run_in_executor(None, model.transform(data_set, 'prediction'))
    #     # result = await model.transform(data_set, 'prediction')
    #     response = result.to_dict(orient='records')
    #     return jsonify(response)
    # except Exception as e:
    #     return jsonify({'error': str(e)})
        result = await model.transform(data_set, 'prediction')
        response = result.to_dict(orient='records')
        return jsonify(response)
    except Exception as e:
        return jsonify({'error': str(e)})
    # # predictions, _ = await op.do_work(model, data_set)
    # prediction_coroutine, _ = op.do_work(model, data_set)
    # async with prediction_coroutine as prediction:
    #     prediction_dict = prediction.to_dict()
    #     return jsonify(prediction_dict)
    # result = predictions.to_dict(orient="records")
    # return jsonify(result)

class LinearSVCModel(Model):
    def __init__(self, user_id, process_id, output_port_id, features, label, label_type, neg, pos, **kwargs):
        self.label = label
        self.label_type = label_type
        self.neg = neg
        self.pos = pos
        self.coefficients = kwargs["coefficients"]
        self.intercept = kwargs["intercept"]
        super(LinearSVCModel, self).__init__(user_id, process_id, output_port_id, features)

    async def transform(self, unl, table_name):
        lab = await super(LinearSVCModel, self).transform(unl, table_name)
        prediction = "`prediction_%s`" % self.label[1:-1]
        linear_item_sql_list = ["`%s` * (%f)" % (self.features[i], self.coefficients[i]) for i in
                                range(len(self.features))]
        linear_sql = "SELECT %s + %f AS hr_temp_col_linear, * FROM %s" % (
            " + ".join(linear_item_sql_list), self.intercept, lab.hr_view)

        lab.loc[prediction] = [self.label_type, "prediction",
                               "CASE WHEN hr_temp_col_linear >= 0 THEN '%s' ELSE '%s' END" % (
                                   self.pos, self.neg)]
        lab = lab.hr_sort_by_role()
        await lab.hr_update_view(table_name, lab.hr_get_view_sql(from_="(%s) hr_temp_table_lsvc_model" % linear_sql))
        return lab

Asked By: Emroy

||

Answers:

I used the async and await keywords to define a view function and used the await keyword on the return value to wait for the result of the model.transform(data_set, ‘prediction’) coroutine and return it as a response. I checked that await was also used in the model.transform() method to wait for the completion of the asynchronous function. However, I didn’t understand why the asynchronous function still returns a coroutine object instead of a value. Running the following script:

import flask
print(flask.__version__)
print(hasattr(flask, 'has_websocket_context'))

I found that the Flask version is 2.0.2 and the has_websocket_context attribute is False. So I located the problem to be that the Flask version used in the project does not support asynchronous view functions. Finally, I used a different approach to solve the problem by calling the asynchronous function in a synchronous view function, mainly relying on asyncio.
I recorded the following two methods:

1.Created a new event loop loop and set it as the current event loop. Then, used the run_until_complete method to run the asynchronous function.

loop = asyncio.new_event_loop()
asyncio.set_event_loop(loop)
lab = loop.run_until_complete(model.transform(lab, table_name))

2.Used the asyncio.run function to run the asynchronous function and returned the result of the asynchronous function using the return statement.

lab = asyncio.run(model.transform(lab, table_name))
Answered By: Emroy
Categories: questions Tags: ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.