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:
- defining an asynchronous view function using async def and using await to wait for the results of asynchronous operations;
- using the async_helpers module;
- 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
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))
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:
- defining an asynchronous view function using async def and using await to wait for the results of asynchronous operations;
- using the async_helpers module;
- 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
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))