Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Flask end response and continue processing

Tags:

Is there a way in Flask to send the response to the client and then continue doing some processing? I have a few book-keeping tasks which are to be done, but I don't want to keep the client waiting.

Note that these are actually really fast things I wish to do, thus creating a new thread, or using a queue, isn't really appropriate here. (One of these fast things is actually adding something to a job queue.)

like image 280
edA-qa mort-ora-y Avatar asked Jun 25 '13 09:06

edA-qa mort-ora-y


People also ask

How do I return a response but continue execution?

TLDR; After receiving the API request, create a new thread and run your function there. You can pass your desired data in kwargs.

What does Flask response do?

The Flask response class, appropriately called Response , is rarely used directly by Flask applications. Instead, Flask uses it internally as a container for the response data returned by application route functions, plus some additional information needed to create an HTTP response.

What is end point Flask?

Basically, the "endpoint" is an identifier that is used in determining what logical unit of your code should handle the request. Normally, an endpoint is just the name of a view function. However, you can actually change the endpoint, as is done in the following example.


2 Answers

Sadly teardown callbacks do not execute after the response has been returned to the client:

import flask import time app = flask.Flask("after_response")  @app.teardown_request def teardown(request):     time.sleep(2)     print("teardown_request")  @app.route("/") def home():     return "Success!\n"  if __name__ == "__main__":     app.run() 

When curling this you'll note a 2s delay before the response displays, rather than the curl ending immediately and then a log 2s later. This is further confirmed by the logs:

teardown_request 127.0.0.1 - - [25/Jun/2018 15:41:51] "GET / HTTP/1.1" 200 - 

The correct way to execute after a response is returned is to use WSGI middleware that adds a hook to the close method of the response iterator. This is not quite as simple as the teardown_request decorator, but it's still pretty straight-forward:

import traceback from werkzeug.wsgi import ClosingIterator  class AfterResponse:     def __init__(self, app=None):         self.callbacks = []         if app:             self.init_app(app)      def __call__(self, callback):         self.callbacks.append(callback)         return callback      def init_app(self, app):         # install extension         app.after_response = self          # install middleware         app.wsgi_app = AfterResponseMiddleware(app.wsgi_app, self)      def flush(self):         for fn in self.callbacks:             try:                 fn()             except Exception:                 traceback.print_exc()  class AfterResponseMiddleware:     def __init__(self, application, after_response_ext):         self.application = application         self.after_response_ext = after_response_ext      def __call__(self, environ, start_response):         iterator = self.application(environ, start_response)         try:             return ClosingIterator(iterator, [self.after_response_ext.flush])         except Exception:             traceback.print_exc()             return iterator 

Which you can then use like this:

@app.after_response def after():     time.sleep(2)     print("after_response") 

From the shell you will see the response return immediately and then 2 seconds later the after_response will hit the logs:

127.0.0.1 - - [25/Jun/2018 15:41:51] "GET / HTTP/1.1" 200 - after_response 

This is a summary of a previous answer provided here.

like image 119
Matthew Story Avatar answered Sep 28 '22 02:09

Matthew Story


QUICK and EASY method.

We will use pythons Thread Library to acheive this.

Your API consumer has sent something to process and which is processed by my_task() function which takes 10 seconds to execute. But the consumer of the API wants a response as soon as they hit your API which is return_status() function.

You tie the my_task to a thread and then return the quick response to the API consumer, while in the background the big process gets compelete.

Below is a simple POC.

import os from flask import Flask,jsonify import time from threading import Thread  app = Flask(__name__)  @app.route("/") def main():     return "Welcome!"  @app.route('/add_') def return_status():     """Return first the response and tie the my_task to a thread"""     Thread(target = my_task).start()     return jsonify('Response asynchronosly')  def my_task():     """Big function doing some job here I just put pandas dataframe to csv conversion"""     time.sleep(10)     import pandas as pd     pd.DataFrame(['sameple data']).to_csv('./success.csv')     return print('large function completed')  if __name__ == "__main__":     app.run(host="0.0.0.0", port=8080) 
like image 26
Danish Xavier Avatar answered Sep 28 '22 01:09

Danish Xavier