Need help in using Signals with Python Flask - python

I am new to Python Flask and need some information/basic knowledge on how to use Signals with Flask.
My understanding so far:
I can create and send own signals. I can use this signal, to make a function call.
My Flask Application looks something like this:
#app.route("/")
def start():
return render_template('index.html')
#app.route("/search")
def search():
rThread = threading.Thread(target=getTags)
rThread.start()
return redirect(url_for('search'))
The getTags in rThread does somemthing outside the application.
But basically it looks like this:
def getTags():
#do something#
#now its finished#
I want to send a signal when getTags is done and the Flask application should get this signal and redirect to index.html.
I am stuck on creating a signal, but I have no clue how to send the signal and how to tell the flask app to redirect to index.html if the signal is sent.
Thanks for any help and advice.
EDIT: For clarification, there are some more pages a user can go to.
#app.route("/details")
def det():
#do stuff
return render_template('details.html')
#app.route("/admin")
def adm():
if request.method == "GET":
#do Stuff
return redirect(url_for('search'))
else
#do Stuff
return render_template("admin.html")

If you just want to wait for a function to do its job you can simply call the function and then respond after it. You used threading.thread so you can use threading.Event and wait for the event to set by the function:
from flask import Flask, render_template, url_for, redirect
from threading import Thread, Event
from time import sleep
app = Flask(__name__)
event = Event()
#app.route("/")
def start():
render_template('index.html')
#app.route("/search")
def search():
rThread = Thread(target=getTags)
rThread.start()
event.wait()
event.clear()
return redirect(url_for('start'))
def getTags():
print("doing some processing things")
sleep(5)
print("done")
event.set()
if __name__ == "__main__":
event.clear()
app.run(host="localhost", port="8080", debug=True)

It not possible for a web server to trigger an event in client or web browser.
Write a java script function that run for certain interval of time and get data from server and then if the result is required then you redirect to another page.

Related

Can I run a function after template rendering in Flask [duplicate]

I have some code that needs to execute after Flask returns a response. I don't think it's complex enough to set up a task queue like Celery for it. The key requirement is that Flask must return the response to the client before running this function. It can't wait for the function to execute.
There are some existing questions about this, but none of the answers seem to address running a task after the response is sent to the client, they still execute synchronously and then the response is returned.
Python Flask sending response immediately
Need to execute a function after returning the response in Flask
Flask end response and continue processing
The long story short is that Flask does not provide any special capabilities to accomplish this. For simple one-off tasks, consider Python's multithreading as shown below. For more complex configurations, use a task queue like RQ or Celery.
Why?
It's important to understand the functions Flask provides and why they do not accomplish the intended goal. All of these are useful in other cases and are good reading, but don't help with background tasks.
Flask's after_request handler
Flask's after_request handler, as detailed in this pattern for deferred request callbacks and this snippet on attaching different functions per request, will pass the request to the callback function. The intended use case is to modify the request, such as to attach a cookie.
Thus the request will wait around for these handlers to finish executing because the expectation is that the request itself will change as a result.
Flask's teardown_request handler
This is similar to after_request, but teardown_request doesn't receive the request object. So that means it won't wait for the request, right?
This seems like the solution, as this answer to a similar Stack Overflow question suggests. And since Flask's documentation explains that teardown callbacks are independent of the actual request and do not receive the request context, you'd have good reason to believe this.
Unfortunately, teardown_request is still synchronous, it just happens at a later part of Flask's request handling when the request is no longer modifiable. Flask will still wait for teardown functions to complete before returning the response, as this list of Flask callbacks and errors dictates.
Flask's streaming responses
Flask can stream responses by passing a generator to Response(), as this Stack Overflow answer to a similar question suggests.
With streaming, the client does begin receiving the response before the request concludes. However, the request still runs synchronously, so the worker handling the request is busy until the stream is finished.
This Flask pattern for streaming includes some documentation on using stream_with_context(), which is necessary to include the request context.
So what's the solution?
Flask doesn't offer a solution to run functions in the background because this isn't Flask's responsibility.
In most cases, the best way to solve this problem is to use a task queue such as RQ or Celery. These manage tricky things like configuration, scheduling, and distributing workers for you.This is the most common answer to this type of question because it is the most correct, and forces you to set things up in a way where you consider context, etc. correctly.
If you need to run a function in the background and don't want to set up a queue to manage this, you can use Python's built in threading or multiprocessing to spawn a background worker.
You can't access request or others of Flask's thread locals from background tasks, since the request will not be active there. Instead, pass the data you need from the view to the background thread when you create it.
#app.route('/start_task')
def start_task():
def do_work(value):
# do something that takes a long time
import time
time.sleep(value)
thread = Thread(target=do_work, kwargs={'value': request.args.get('value', 20)})
thread.start()
return 'started'
Flask is a WSGI app and as a result it fundamentally cannot handle anything after the response. This is why no such handler exists, the WSGI app itself is responsible only for constructing the response iterator object to the WSGI server.
A WSGI server however (like gunicorn) can very easily provide this functionality, but tying the application to the server is a very bad idea for a number of reasons.
For this exact reason, WSGI provides a spec for Middleware, and Werkzeug provides a number of helpers to simplify common Middleware functionality. Among them is a ClosingIterator class which allows you to hook methods up to the close method of the response iterator which is executed after the request is closed.
Here's an example of a naive after_response implementation done as a Flask extension:
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, after_response):
iterator = self.application(environ, after_response)
try:
return ClosingIterator(iterator, [self.after_response_ext.flush])
except Exception:
traceback.print_exc()
return iterator
You can use this extension like this:
import flask
app = flask.Flask("after_response")
AfterResponse(app)
#app.after_response
def say_hi():
print("hi")
#app.route("/")
def home():
return "Success!\n"
When you curl "/" you'll see the following in your logs:
127.0.0.1 - - [24/Jun/2018 19:30:48] "GET / HTTP/1.1" 200 -
hi
This solves the issue simply without introducing either threads (GIL??) or having to install and manage a task queue and client software.
Flask now supports (via Werkzeug) a call_on_close callback decorator on response objects. Here is how you use it:
#app.after_request
def response_processor(response):
# Prepare all the local variables you need since the request context
# will be gone in the callback function
#response.call_on_close
def process_after_request():
# Do whatever is necessary here
pass
return response
Advantages:
call_on_close sets up functions for being called after the response is returned, using the WSGI spec for the close method.
No threads, no background jobs, no complicated setup. It runs in the same thread without blocking the request from returning.
Disadvantages:
No request context or app context. You have to save the variables you need, to pass into the closure.
No local stack as all that is being torn down. You have to make your own app context if you need it.
Flask-SQLAlchemy will fail silently if you're attempting to write to the database (I haven't figured out why, but likely due to the context shutting down). (It works, but if you have an existing object, it must be added to the new session using session.add or session.merge; not a disadvantage!)
There are 3 ways to do this, all work:
1. Thread
#app.route('/inner')
def foo():
for i in range(10):
sleep(1)
print(i)
return
#app.route('/inner', methods=['POST'])
def run_jobs():
try:
thread = Thread(target=foo)
thread.start()
return render_template("index_inner.html", img_path=DIR_OF_PHOTOS, video_path=UPLOAD_VIDEOS_FOLDER)
2. AfterResponse decorator
app = Flask(__name__)
AfterResponse(app)
#app.route('/inner', methods=['POST'])
def save_data():
pass
#app.after_response
def foo():
for i in range(10):
sleep(1)
print(i)
return
3. call_on_close
from time import sleep
from flask import Flask, Response, request
app = Flask('hello')
#app.route('/')
def hello():
response = Response('hello')
#response.call_on_close
def on_close():
for i in range(10):
sleep(1)
print(i)
return response
if __name__ == '__main__':
app.run()
Middleware Solution for Flask Blueprints
This is the same solution proposed by Matthew Story (which is the perfect solution IMHO - thanks Matthew), adapted for Flask Blueprints. The secret sauce here is to get hold of the app context using the current_app proxy. Read up here for more information (http://flask.pocoo.org/docs/1.0/appcontext/)
Let's assume the AfterThisResponse & AfterThisResponseMiddleware classes are placed in a module at .utils.after_this_response.py
Then where the Flask object creation occurs, you might have, eg...
__init__.py
from api.routes import my_blueprint
from .utils.after_this_response import AfterThisResponse
app = Flask( __name__ )
AfterThisResponse( app )
app.register_blueprint( my_blueprint.mod )
And then in your blueprint module...
a_blueprint.py
from flask import Blueprint, current_app
mod = Blueprint( 'a_blueprint', __name__, url_prefix=URL_PREFIX )
#mod.route( "/some_resource", methods=['GET', 'POST'] )
def some_resource():
# do some stuff here if you want
#current_app.after_this_response
def post_process():
# this will occur after you finish processing the route & return (below):
time.sleep(2)
print("after_response")
# do more stuff here if you like & then return like so:
return "Success!\n"
In addition to the other solutions, you can do route specific actions by combining after_this_request and response.call_on_close:
#app.route('/')
def index():
# Do your pre-response work here
msg = 'Hello World!'
#flask.after_this_request
def add_close_action(response):
#response.call_on_close
def process_after_request():
# Do your post-response work here
time.sleep(3.0)
print('Delayed: ' + msg)
return response
return msg
Thanks to Matthew Story and Paul Brackin, but I needed to change their proposals.
So the working solution is:
.
├── __init__.py
├── blueprint.py
└── library.py
# __init__.py
from flask import Flask
from .blueprint import bp
from .library import AfterResponse
app = Flask(__name__)
with app.app_context():
app.register_blueprint(bp, url_prefix='/')
AfterResponse(app)
# blueprint.py
from flask import Blueprint, request, current_app as app
from time import sleep
bp = Blueprint('app', __name__)
#bp.route('/')
def root():
body = request.json
#app.after_response
def worker():
print(body)
sleep(5)
print('finished_after_processing')
print('returned')
return 'finished_fast', 200
# library.py
from werkzeug.wsgi import ClosingIterator
from traceback import print_exc
class AfterResponse:
def __init__(self, application=None):
self.functions = list()
if application:
self.init_app(application)
def __call__(self, function):
self.functions.append(function)
def init_app(self, application):
application.after_response = self
application.wsgi_app = AfterResponseMiddleware(application.wsgi_app, self)
def flush(self):
while self.functions:
try:
self.functions.pop()()
except Exception:
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, after_response):
iterator = self.application(environ, after_response)
try:
return ClosingIterator(iterator, [self.after_response_ext.flush])
except Exception:
print_exc()
return iterator
The source code can be found here
The signal request_finished receives a Response instance as parameter. Any after-processing can be done by connecting to that signal.
From https://flask-doc.readthedocs.io/en/latest/signals.html:
def log_response(sender, response, **extra):
sender.logger.debug('Request context is about to close down. '
'Response: %s', response)
from flask import request_finished
request_finished.connect(log_response, app)
Obs: In case of error, the signal got_request_exception can be used instead.
After read many topics.
I found the solution for me, if use Blueprint, it is worked for python 3.8 and SQLAlchemy
init.py
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
from flask_login import LoginManager
import dir
import time
from flask_mail import Mail
from flask_cors import CORS
import flask_excel as excel
# init SQLAlchemy so we can use it later in our models
dbb = SQLAlchemy()
def create_app():
app = Flask(__name__)
from .bp_route_1 import auth as bp_route_1_blueprint
app.register_blueprint(bp_route_1_blueprint)
CORS(app)
return app
bp_route_1.py
from flask import Blueprint, request, redirect, Response, url_for, abort, flash, render_template, \
copy_current_request_context
from . import dbb
from .models import #Import Models
from threading import Thread
bp_route_1 = Blueprint('bp_route_1', __name__)
#bp_route_1.route('/wehooks', methods=['POST'])
def route_1_wehooks_post():
#copy_current_request_context #to copy request
def foo_main():
# insert your code here
do_long_time_webhook(request)
Thread(target=foo_main).start()
print("do Webhook by Thread")
return Response(status=200)
def do_long_time_webhook(request):
try:
data = request.get_data()
print(data)
#do long tim function for webhook data
except Exception as e:
print('Dont do webhook', e)
To expand upon Kiran's response, I've added the call_on_close decorator to the copy_current_request_context decorator along with their imports.
I can confirm this works as expected.
from flask import Response, copy_current_request_context
#app.route('/example-route')
def response():
# Prepare response
#response.call_on_close
#copy_current_request_context
def post_processing():
# Process after response
pass
return response
You can use this code i have tried it.It works.
this code will print the string "message". after the 3 second ,from the scheduling time. You can change the time your self according to you requirement.
import time, traceback
import threading
def every(delay,message, task):
next_time = time.time() + delay
time.sleep(max(0, next_time - time.time()))
task(message)
def foo(message):
print(message+" :foo", time.time())
def main(message):
threading.Thread(target=lambda: every(3,message, foo)).start()
main("message")

Python/Flask Timed Event Configuration Page

I'm trying to write a Python application that will execute certain code repeatedly on a timer when that timer is enabled. I'd like to be able to enable/disable the timer from a web page. I've been attempting to do this with Flask and the sched event scheduler, to no avail. I'm not sure if either Flask or sched are the right tools for the job, and I appreciate any guidance. Thanks!
My Code So Far:
from flask import Flask, render_template, request
import sched, time
app = Flask(__name__)
cameraScheduler = sched.scheduler(time.time, time.sleep)
def triggerCamera(sc):
print('Picture Taken')
cameraScheduler.enter(5,1,triggerCamera,(sc,))
#app.route('/', methods = ['POST','GET'])
def settings():
if request.method == 'POST':
if(request.form.getlist('timer') == ['true']):
cameraScheduler.enter(5,1,triggerCamera,(cameraScheduler,))
cameraScheduler.run
else:
cameraScheduler.cancel()
return render_template("settings.html")
if __name__ == '__main__':
app.run()
Try actually calling the scheduler cameraScheduler.run() (you are missing the parentheses there).

Call a request in Python without waiting [duplicate]

I have some code that needs to execute after Flask returns a response. I don't think it's complex enough to set up a task queue like Celery for it. The key requirement is that Flask must return the response to the client before running this function. It can't wait for the function to execute.
There are some existing questions about this, but none of the answers seem to address running a task after the response is sent to the client, they still execute synchronously and then the response is returned.
Python Flask sending response immediately
Need to execute a function after returning the response in Flask
Flask end response and continue processing
The long story short is that Flask does not provide any special capabilities to accomplish this. For simple one-off tasks, consider Python's multithreading as shown below. For more complex configurations, use a task queue like RQ or Celery.
Why?
It's important to understand the functions Flask provides and why they do not accomplish the intended goal. All of these are useful in other cases and are good reading, but don't help with background tasks.
Flask's after_request handler
Flask's after_request handler, as detailed in this pattern for deferred request callbacks and this snippet on attaching different functions per request, will pass the request to the callback function. The intended use case is to modify the request, such as to attach a cookie.
Thus the request will wait around for these handlers to finish executing because the expectation is that the request itself will change as a result.
Flask's teardown_request handler
This is similar to after_request, but teardown_request doesn't receive the request object. So that means it won't wait for the request, right?
This seems like the solution, as this answer to a similar Stack Overflow question suggests. And since Flask's documentation explains that teardown callbacks are independent of the actual request and do not receive the request context, you'd have good reason to believe this.
Unfortunately, teardown_request is still synchronous, it just happens at a later part of Flask's request handling when the request is no longer modifiable. Flask will still wait for teardown functions to complete before returning the response, as this list of Flask callbacks and errors dictates.
Flask's streaming responses
Flask can stream responses by passing a generator to Response(), as this Stack Overflow answer to a similar question suggests.
With streaming, the client does begin receiving the response before the request concludes. However, the request still runs synchronously, so the worker handling the request is busy until the stream is finished.
This Flask pattern for streaming includes some documentation on using stream_with_context(), which is necessary to include the request context.
So what's the solution?
Flask doesn't offer a solution to run functions in the background because this isn't Flask's responsibility.
In most cases, the best way to solve this problem is to use a task queue such as RQ or Celery. These manage tricky things like configuration, scheduling, and distributing workers for you.This is the most common answer to this type of question because it is the most correct, and forces you to set things up in a way where you consider context, etc. correctly.
If you need to run a function in the background and don't want to set up a queue to manage this, you can use Python's built in threading or multiprocessing to spawn a background worker.
You can't access request or others of Flask's thread locals from background tasks, since the request will not be active there. Instead, pass the data you need from the view to the background thread when you create it.
#app.route('/start_task')
def start_task():
def do_work(value):
# do something that takes a long time
import time
time.sleep(value)
thread = Thread(target=do_work, kwargs={'value': request.args.get('value', 20)})
thread.start()
return 'started'
Flask is a WSGI app and as a result it fundamentally cannot handle anything after the response. This is why no such handler exists, the WSGI app itself is responsible only for constructing the response iterator object to the WSGI server.
A WSGI server however (like gunicorn) can very easily provide this functionality, but tying the application to the server is a very bad idea for a number of reasons.
For this exact reason, WSGI provides a spec for Middleware, and Werkzeug provides a number of helpers to simplify common Middleware functionality. Among them is a ClosingIterator class which allows you to hook methods up to the close method of the response iterator which is executed after the request is closed.
Here's an example of a naive after_response implementation done as a Flask extension:
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, after_response):
iterator = self.application(environ, after_response)
try:
return ClosingIterator(iterator, [self.after_response_ext.flush])
except Exception:
traceback.print_exc()
return iterator
You can use this extension like this:
import flask
app = flask.Flask("after_response")
AfterResponse(app)
#app.after_response
def say_hi():
print("hi")
#app.route("/")
def home():
return "Success!\n"
When you curl "/" you'll see the following in your logs:
127.0.0.1 - - [24/Jun/2018 19:30:48] "GET / HTTP/1.1" 200 -
hi
This solves the issue simply without introducing either threads (GIL??) or having to install and manage a task queue and client software.
Flask now supports (via Werkzeug) a call_on_close callback decorator on response objects. Here is how you use it:
#app.after_request
def response_processor(response):
# Prepare all the local variables you need since the request context
# will be gone in the callback function
#response.call_on_close
def process_after_request():
# Do whatever is necessary here
pass
return response
Advantages:
call_on_close sets up functions for being called after the response is returned, using the WSGI spec for the close method.
No threads, no background jobs, no complicated setup. It runs in the same thread without blocking the request from returning.
Disadvantages:
No request context or app context. You have to save the variables you need, to pass into the closure.
No local stack as all that is being torn down. You have to make your own app context if you need it.
Flask-SQLAlchemy will fail silently if you're attempting to write to the database (I haven't figured out why, but likely due to the context shutting down). (It works, but if you have an existing object, it must be added to the new session using session.add or session.merge; not a disadvantage!)
There are 3 ways to do this, all work:
1. Thread
#app.route('/inner')
def foo():
for i in range(10):
sleep(1)
print(i)
return
#app.route('/inner', methods=['POST'])
def run_jobs():
try:
thread = Thread(target=foo)
thread.start()
return render_template("index_inner.html", img_path=DIR_OF_PHOTOS, video_path=UPLOAD_VIDEOS_FOLDER)
2. AfterResponse decorator
app = Flask(__name__)
AfterResponse(app)
#app.route('/inner', methods=['POST'])
def save_data():
pass
#app.after_response
def foo():
for i in range(10):
sleep(1)
print(i)
return
3. call_on_close
from time import sleep
from flask import Flask, Response, request
app = Flask('hello')
#app.route('/')
def hello():
response = Response('hello')
#response.call_on_close
def on_close():
for i in range(10):
sleep(1)
print(i)
return response
if __name__ == '__main__':
app.run()
Middleware Solution for Flask Blueprints
This is the same solution proposed by Matthew Story (which is the perfect solution IMHO - thanks Matthew), adapted for Flask Blueprints. The secret sauce here is to get hold of the app context using the current_app proxy. Read up here for more information (http://flask.pocoo.org/docs/1.0/appcontext/)
Let's assume the AfterThisResponse & AfterThisResponseMiddleware classes are placed in a module at .utils.after_this_response.py
Then where the Flask object creation occurs, you might have, eg...
__init__.py
from api.routes import my_blueprint
from .utils.after_this_response import AfterThisResponse
app = Flask( __name__ )
AfterThisResponse( app )
app.register_blueprint( my_blueprint.mod )
And then in your blueprint module...
a_blueprint.py
from flask import Blueprint, current_app
mod = Blueprint( 'a_blueprint', __name__, url_prefix=URL_PREFIX )
#mod.route( "/some_resource", methods=['GET', 'POST'] )
def some_resource():
# do some stuff here if you want
#current_app.after_this_response
def post_process():
# this will occur after you finish processing the route & return (below):
time.sleep(2)
print("after_response")
# do more stuff here if you like & then return like so:
return "Success!\n"
In addition to the other solutions, you can do route specific actions by combining after_this_request and response.call_on_close:
#app.route('/')
def index():
# Do your pre-response work here
msg = 'Hello World!'
#flask.after_this_request
def add_close_action(response):
#response.call_on_close
def process_after_request():
# Do your post-response work here
time.sleep(3.0)
print('Delayed: ' + msg)
return response
return msg
Thanks to Matthew Story and Paul Brackin, but I needed to change their proposals.
So the working solution is:
.
├── __init__.py
├── blueprint.py
└── library.py
# __init__.py
from flask import Flask
from .blueprint import bp
from .library import AfterResponse
app = Flask(__name__)
with app.app_context():
app.register_blueprint(bp, url_prefix='/')
AfterResponse(app)
# blueprint.py
from flask import Blueprint, request, current_app as app
from time import sleep
bp = Blueprint('app', __name__)
#bp.route('/')
def root():
body = request.json
#app.after_response
def worker():
print(body)
sleep(5)
print('finished_after_processing')
print('returned')
return 'finished_fast', 200
# library.py
from werkzeug.wsgi import ClosingIterator
from traceback import print_exc
class AfterResponse:
def __init__(self, application=None):
self.functions = list()
if application:
self.init_app(application)
def __call__(self, function):
self.functions.append(function)
def init_app(self, application):
application.after_response = self
application.wsgi_app = AfterResponseMiddleware(application.wsgi_app, self)
def flush(self):
while self.functions:
try:
self.functions.pop()()
except Exception:
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, after_response):
iterator = self.application(environ, after_response)
try:
return ClosingIterator(iterator, [self.after_response_ext.flush])
except Exception:
print_exc()
return iterator
The source code can be found here
The signal request_finished receives a Response instance as parameter. Any after-processing can be done by connecting to that signal.
From https://flask-doc.readthedocs.io/en/latest/signals.html:
def log_response(sender, response, **extra):
sender.logger.debug('Request context is about to close down. '
'Response: %s', response)
from flask import request_finished
request_finished.connect(log_response, app)
Obs: In case of error, the signal got_request_exception can be used instead.
After read many topics.
I found the solution for me, if use Blueprint, it is worked for python 3.8 and SQLAlchemy
init.py
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
from flask_login import LoginManager
import dir
import time
from flask_mail import Mail
from flask_cors import CORS
import flask_excel as excel
# init SQLAlchemy so we can use it later in our models
dbb = SQLAlchemy()
def create_app():
app = Flask(__name__)
from .bp_route_1 import auth as bp_route_1_blueprint
app.register_blueprint(bp_route_1_blueprint)
CORS(app)
return app
bp_route_1.py
from flask import Blueprint, request, redirect, Response, url_for, abort, flash, render_template, \
copy_current_request_context
from . import dbb
from .models import #Import Models
from threading import Thread
bp_route_1 = Blueprint('bp_route_1', __name__)
#bp_route_1.route('/wehooks', methods=['POST'])
def route_1_wehooks_post():
#copy_current_request_context #to copy request
def foo_main():
# insert your code here
do_long_time_webhook(request)
Thread(target=foo_main).start()
print("do Webhook by Thread")
return Response(status=200)
def do_long_time_webhook(request):
try:
data = request.get_data()
print(data)
#do long tim function for webhook data
except Exception as e:
print('Dont do webhook', e)
To expand upon Kiran's response, I've added the call_on_close decorator to the copy_current_request_context decorator along with their imports.
I can confirm this works as expected.
from flask import Response, copy_current_request_context
#app.route('/example-route')
def response():
# Prepare response
#response.call_on_close
#copy_current_request_context
def post_processing():
# Process after response
pass
return response
You can use this code i have tried it.It works.
this code will print the string "message". after the 3 second ,from the scheduling time. You can change the time your self according to you requirement.
import time, traceback
import threading
def every(delay,message, task):
next_time = time.time() + delay
time.sleep(max(0, next_time - time.time()))
task(message)
def foo(message):
print(message+" :foo", time.time())
def main(message):
threading.Thread(target=lambda: every(3,message, foo)).start()
main("message")

Execute a function after Flask returns response

I have some code that needs to execute after Flask returns a response. I don't think it's complex enough to set up a task queue like Celery for it. The key requirement is that Flask must return the response to the client before running this function. It can't wait for the function to execute.
There are some existing questions about this, but none of the answers seem to address running a task after the response is sent to the client, they still execute synchronously and then the response is returned.
Python Flask sending response immediately
Need to execute a function after returning the response in Flask
Flask end response and continue processing
The long story short is that Flask does not provide any special capabilities to accomplish this. For simple one-off tasks, consider Python's multithreading as shown below. For more complex configurations, use a task queue like RQ or Celery.
Why?
It's important to understand the functions Flask provides and why they do not accomplish the intended goal. All of these are useful in other cases and are good reading, but don't help with background tasks.
Flask's after_request handler
Flask's after_request handler, as detailed in this pattern for deferred request callbacks and this snippet on attaching different functions per request, will pass the request to the callback function. The intended use case is to modify the request, such as to attach a cookie.
Thus the request will wait around for these handlers to finish executing because the expectation is that the request itself will change as a result.
Flask's teardown_request handler
This is similar to after_request, but teardown_request doesn't receive the request object. So that means it won't wait for the request, right?
This seems like the solution, as this answer to a similar Stack Overflow question suggests. And since Flask's documentation explains that teardown callbacks are independent of the actual request and do not receive the request context, you'd have good reason to believe this.
Unfortunately, teardown_request is still synchronous, it just happens at a later part of Flask's request handling when the request is no longer modifiable. Flask will still wait for teardown functions to complete before returning the response, as this list of Flask callbacks and errors dictates.
Flask's streaming responses
Flask can stream responses by passing a generator to Response(), as this Stack Overflow answer to a similar question suggests.
With streaming, the client does begin receiving the response before the request concludes. However, the request still runs synchronously, so the worker handling the request is busy until the stream is finished.
This Flask pattern for streaming includes some documentation on using stream_with_context(), which is necessary to include the request context.
So what's the solution?
Flask doesn't offer a solution to run functions in the background because this isn't Flask's responsibility.
In most cases, the best way to solve this problem is to use a task queue such as RQ or Celery. These manage tricky things like configuration, scheduling, and distributing workers for you.This is the most common answer to this type of question because it is the most correct, and forces you to set things up in a way where you consider context, etc. correctly.
If you need to run a function in the background and don't want to set up a queue to manage this, you can use Python's built in threading or multiprocessing to spawn a background worker.
You can't access request or others of Flask's thread locals from background tasks, since the request will not be active there. Instead, pass the data you need from the view to the background thread when you create it.
#app.route('/start_task')
def start_task():
def do_work(value):
# do something that takes a long time
import time
time.sleep(value)
thread = Thread(target=do_work, kwargs={'value': request.args.get('value', 20)})
thread.start()
return 'started'
Flask is a WSGI app and as a result it fundamentally cannot handle anything after the response. This is why no such handler exists, the WSGI app itself is responsible only for constructing the response iterator object to the WSGI server.
A WSGI server however (like gunicorn) can very easily provide this functionality, but tying the application to the server is a very bad idea for a number of reasons.
For this exact reason, WSGI provides a spec for Middleware, and Werkzeug provides a number of helpers to simplify common Middleware functionality. Among them is a ClosingIterator class which allows you to hook methods up to the close method of the response iterator which is executed after the request is closed.
Here's an example of a naive after_response implementation done as a Flask extension:
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, after_response):
iterator = self.application(environ, after_response)
try:
return ClosingIterator(iterator, [self.after_response_ext.flush])
except Exception:
traceback.print_exc()
return iterator
You can use this extension like this:
import flask
app = flask.Flask("after_response")
AfterResponse(app)
#app.after_response
def say_hi():
print("hi")
#app.route("/")
def home():
return "Success!\n"
When you curl "/" you'll see the following in your logs:
127.0.0.1 - - [24/Jun/2018 19:30:48] "GET / HTTP/1.1" 200 -
hi
This solves the issue simply without introducing either threads (GIL??) or having to install and manage a task queue and client software.
Flask now supports (via Werkzeug) a call_on_close callback decorator on response objects. Here is how you use it:
#app.after_request
def response_processor(response):
# Prepare all the local variables you need since the request context
# will be gone in the callback function
#response.call_on_close
def process_after_request():
# Do whatever is necessary here
pass
return response
Advantages:
call_on_close sets up functions for being called after the response is returned, using the WSGI spec for the close method.
No threads, no background jobs, no complicated setup. It runs in the same thread without blocking the request from returning.
Disadvantages:
No request context or app context. You have to save the variables you need, to pass into the closure.
No local stack as all that is being torn down. You have to make your own app context if you need it.
Flask-SQLAlchemy will fail silently if you're attempting to write to the database (I haven't figured out why, but likely due to the context shutting down). (It works, but if you have an existing object, it must be added to the new session using session.add or session.merge; not a disadvantage!)
There are 3 ways to do this, all work:
1. Thread
#app.route('/inner')
def foo():
for i in range(10):
sleep(1)
print(i)
return
#app.route('/inner', methods=['POST'])
def run_jobs():
try:
thread = Thread(target=foo)
thread.start()
return render_template("index_inner.html", img_path=DIR_OF_PHOTOS, video_path=UPLOAD_VIDEOS_FOLDER)
2. AfterResponse decorator
app = Flask(__name__)
AfterResponse(app)
#app.route('/inner', methods=['POST'])
def save_data():
pass
#app.after_response
def foo():
for i in range(10):
sleep(1)
print(i)
return
3. call_on_close
from time import sleep
from flask import Flask, Response, request
app = Flask('hello')
#app.route('/')
def hello():
response = Response('hello')
#response.call_on_close
def on_close():
for i in range(10):
sleep(1)
print(i)
return response
if __name__ == '__main__':
app.run()
Middleware Solution for Flask Blueprints
This is the same solution proposed by Matthew Story (which is the perfect solution IMHO - thanks Matthew), adapted for Flask Blueprints. The secret sauce here is to get hold of the app context using the current_app proxy. Read up here for more information (http://flask.pocoo.org/docs/1.0/appcontext/)
Let's assume the AfterThisResponse & AfterThisResponseMiddleware classes are placed in a module at .utils.after_this_response.py
Then where the Flask object creation occurs, you might have, eg...
__init__.py
from api.routes import my_blueprint
from .utils.after_this_response import AfterThisResponse
app = Flask( __name__ )
AfterThisResponse( app )
app.register_blueprint( my_blueprint.mod )
And then in your blueprint module...
a_blueprint.py
from flask import Blueprint, current_app
mod = Blueprint( 'a_blueprint', __name__, url_prefix=URL_PREFIX )
#mod.route( "/some_resource", methods=['GET', 'POST'] )
def some_resource():
# do some stuff here if you want
#current_app.after_this_response
def post_process():
# this will occur after you finish processing the route & return (below):
time.sleep(2)
print("after_response")
# do more stuff here if you like & then return like so:
return "Success!\n"
In addition to the other solutions, you can do route specific actions by combining after_this_request and response.call_on_close:
#app.route('/')
def index():
# Do your pre-response work here
msg = 'Hello World!'
#flask.after_this_request
def add_close_action(response):
#response.call_on_close
def process_after_request():
# Do your post-response work here
time.sleep(3.0)
print('Delayed: ' + msg)
return response
return msg
Thanks to Matthew Story and Paul Brackin, but I needed to change their proposals.
So the working solution is:
.
├── __init__.py
├── blueprint.py
└── library.py
# __init__.py
from flask import Flask
from .blueprint import bp
from .library import AfterResponse
app = Flask(__name__)
with app.app_context():
app.register_blueprint(bp, url_prefix='/')
AfterResponse(app)
# blueprint.py
from flask import Blueprint, request, current_app as app
from time import sleep
bp = Blueprint('app', __name__)
#bp.route('/')
def root():
body = request.json
#app.after_response
def worker():
print(body)
sleep(5)
print('finished_after_processing')
print('returned')
return 'finished_fast', 200
# library.py
from werkzeug.wsgi import ClosingIterator
from traceback import print_exc
class AfterResponse:
def __init__(self, application=None):
self.functions = list()
if application:
self.init_app(application)
def __call__(self, function):
self.functions.append(function)
def init_app(self, application):
application.after_response = self
application.wsgi_app = AfterResponseMiddleware(application.wsgi_app, self)
def flush(self):
while self.functions:
try:
self.functions.pop()()
except Exception:
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, after_response):
iterator = self.application(environ, after_response)
try:
return ClosingIterator(iterator, [self.after_response_ext.flush])
except Exception:
print_exc()
return iterator
The source code can be found here
The signal request_finished receives a Response instance as parameter. Any after-processing can be done by connecting to that signal.
From https://flask-doc.readthedocs.io/en/latest/signals.html:
def log_response(sender, response, **extra):
sender.logger.debug('Request context is about to close down. '
'Response: %s', response)
from flask import request_finished
request_finished.connect(log_response, app)
Obs: In case of error, the signal got_request_exception can be used instead.
After read many topics.
I found the solution for me, if use Blueprint, it is worked for python 3.8 and SQLAlchemy
init.py
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
from flask_login import LoginManager
import dir
import time
from flask_mail import Mail
from flask_cors import CORS
import flask_excel as excel
# init SQLAlchemy so we can use it later in our models
dbb = SQLAlchemy()
def create_app():
app = Flask(__name__)
from .bp_route_1 import auth as bp_route_1_blueprint
app.register_blueprint(bp_route_1_blueprint)
CORS(app)
return app
bp_route_1.py
from flask import Blueprint, request, redirect, Response, url_for, abort, flash, render_template, \
copy_current_request_context
from . import dbb
from .models import #Import Models
from threading import Thread
bp_route_1 = Blueprint('bp_route_1', __name__)
#bp_route_1.route('/wehooks', methods=['POST'])
def route_1_wehooks_post():
#copy_current_request_context #to copy request
def foo_main():
# insert your code here
do_long_time_webhook(request)
Thread(target=foo_main).start()
print("do Webhook by Thread")
return Response(status=200)
def do_long_time_webhook(request):
try:
data = request.get_data()
print(data)
#do long tim function for webhook data
except Exception as e:
print('Dont do webhook', e)
To expand upon Kiran's response, I've added the call_on_close decorator to the copy_current_request_context decorator along with their imports.
I can confirm this works as expected.
from flask import Response, copy_current_request_context
#app.route('/example-route')
def response():
# Prepare response
#response.call_on_close
#copy_current_request_context
def post_processing():
# Process after response
pass
return response
You can use this code i have tried it.It works.
this code will print the string "message". after the 3 second ,from the scheduling time. You can change the time your self according to you requirement.
import time, traceback
import threading
def every(delay,message, task):
next_time = time.time() + delay
time.sleep(max(0, next_time - time.time()))
task(message)
def foo(message):
print(message+" :foo", time.time())
def main(message):
threading.Thread(target=lambda: every(3,message, foo)).start()
main("message")

Python flask form submit function to background process

I would like to know how can we do the process after the form submit as a background process.
from flask import Flask, g, redirect, url_for
#app.route("/form", methods=['GET', 'POST'])
def form():
if request.method == 'POST':
#form processing(This block need to do as background)
#background must have access to the app.* and g.* variables
return redirect(url_for('app.form_result'))
g.result['page_title'] = 'Form Input'
g.template = 'form.html'
return 'Done'
I see two possible solutions:
First, Use APScheduler (https://pypi.python.org/pypi/APScheduler) and schedule the process as a single job:
from apscheduler.schedulers.background import BackgroundScheduler
def processing_function(app, g):
pass # Perform your processing here
scheduler = BackgroundScheduler()
scheduler.start()
scheduler.add_job(processing_function, app, g)
Second, Use Celery (http://www.celeryproject.org/). Use this if you want to run your background process on a separate server. I won't provide a code sample here, but you'll need to start a Celery worker, as well as a broker, both separately from your web server. Then, you can pass jobs back to the worker (with arguments), and have the response sent back.

Categories

Resources