What is the purpose of Flask's context stacks?
Multiple Apps
The application context (and its purpose) is indeed confusing until you realize that Flask can have multiple apps. Imagine the situation where you want to have a single WSGI Python interpreter run multiple Flask application. We're not talking Blueprints here, we're talking entirely different Flask applications.You might set this up similar to the Flask documentation section on "Application Dispatching" example:
from werkzeug.wsgi import DispatcherMiddleware
from frontend_app import application as frontend
from backend_app import application as backend
application = DispatcherMiddleware(frontend, {
'/backend': backend
})
Notice that there are two completely different Flask applications being created "frontend" and "backend". In other words, the Flask(...)
application constructor has been called twice, creating two instances of a Flask application.Contexts
When you are working with Flask, you often end up using global variables to access various functionality. For example, you probably have code that reads...from flask import request
Then, during a view, you might use request
to access the information of the current request. Obviously, request
is not a normal global variable; in actuality, it is a context local value. In other words, there is some magic behind the scenes that says "when I call request.path
, get the path
attribute from the request
object of the CURRENT request." Two different requests will have a different results for request.path
.In fact, even if you run Flask with multiple threads, Flask is smart enough to keep the request objects isolated. In doing so, it becomes possible for two threads, each handling a different request, to simultaneously call request.path
and get the correct information for their respective requests.
Putting it Together
So we've already seen that Flask can handle multiple applications in the same interpreter, and also that because of the way that Flask allows you to use "context local" globals there must be some mechanism to determine what the "current" request is (in order to do things such asrequest.path
).Putting these ideas together, it should also make sense that Flask must have some way to determine what the "current" application is!
You probably also have code similar to the following:
from flask import url_for
Like our request
example, the url_for
function has logic that is dependent on the current environment. In this case, however, it is clear to see that the logic is heavily dependent on which app is considered the "current" app. In the frontend/backend example shown above, both the "frontend" and "backend" apps could have a "/login" route, and so url_for('/login')
should return something different depending on if the view is handling the request for the frontend or backend app.To answer your questions...
From the Request Context docs:What is the purpose of the "stack" when it comes to the request or
application context?
In other words, even though you typically will have 0 or 1 items on these stack of "current" requests or "current" applications, it is possible that you could have more.Because the request context is internally maintained as a stack you
can push and pop multiple times. This is very handy to implement
things like internal redirects.
The example given is where you would have your request return the results of an "internal redirect". Let's say a user requests A, but you want to return to the user B. In most cases, you issue a redirect to the user, and point the user to resource B, meaning the user will run a second request to fetch B. A slightly different way of handling this would be to do an internal redirect, which means that while processing A, Flask will make a new request to itself for resource B, and use the results of this second request as the results of the user's original request.
They are two separate stacks. However, this is an implementation detail. What's more important is not so much that there is a stack, but the fact that at any time you can get the "current" app or request (top of the stack).Are these two separate stacks, or are they both part of one stack?
A "request context" is one item of the "request context stack". Similarly with the "app context" and "app context stack".Is the request context pushed onto a stack, or is it a stack itself?
In a Flask application, you typically would not do this. One example of where you might want to is for an internal redirect (described above). Even in that case, however, you would probably end up having Flask handle a new request, and so Flask would do all of the pushing/popping for you.Am I able to push/pop multiple contexts on top of eachother? If so,
why would I want to do that?
However, there are some cases where you'd want to manipulate the stack yourself.
Running code outside of a request
One typical problem people have is that they use the Flask-SQLAlchemy extension to set up a SQL database and model definition using code something like what is shown below...
app = Flask(__name__)
db = SQLAlchemy() # Initialize the Flask-SQLAlchemy extension object
db.init_app(app)
Then they use the app
and db
values in a script that should be run from the shell. For example, a "setup_tables.py" script...from myapp import app, db
# Set up models
db.create_all()
In this case, the Flask-SQLAlchemy extension knows about the app
application, but during create_all()
it will throw an error complaining about there not being an application context. This error is justified; you never told Flask what application it should be dealing with when running the create_all
method.You might be wondering why you don't end up needing this with app.app_context()
call when you run similar functions in your views. The reason is that Flask already handles the management of the application context for you when it is handling actual web requests. The problem really only comes up outside of these view functions (or other such callbacks), such as when using your models in a one-off script.
The resolution is to push the application context yourself, which can be done by doing...
from myapp import app, db
# Set up models
with app.app_context():
db.create_all()
This will push a new application context (using the application of app
, remember there could be more than one application).Testing
Another case where you would want to manipulate the stack is for testing. You could create a unit test that handles a request and you check the results:
import unittest
from flask import request
class MyTest(unittest.TestCase):
def test_thing(self):
with app.test_request_context('/?next=http://example.com/') as ctx:
# You can now view attributes on request context stack by using `request`.
# Now the request context stack is empty
What's the difference between application and request contexts?
It is true in the request lifecycle. Flask create the app context, the request context, do some magic, destroy request context, destroy app context.Both are created on request and torn down when it finishes.
The application context can exist without a request and that is the reason you have both. For example, if I'm running from a shell, I can create the app_context
, without the request and has access to the ´current_app` proxy.
It is a design decision to separate concerns and give you the option to not create the request context. The request context is expensive.
In old Flask's (0.7?), you had only the request context and created the current_app
with a Werkzeug proxy. So the application context just create a pattern.
Some docs about appcontext, but you probably already read it: http://flask.pocoo.org/docs/appcontext/
Why app context in flask not a singleton for an app?
The app context is not meant for sharing between requests. It is there to share context before the request context is set up, as well as after the request has been torn down already. Yes, this means that there can be multiple g
contexts active for different requests.
You can't share 'global' state because a WSGI app is not limited to a single process. Many WSGI servers use multiprocessing to scale request handling, not just threading. If you need to share 'global' state across requests, use something like a database or memcached.
Flask, push application context for Flask-sqlalchemy to huey worker
It is better if you let Huey create a Flask app for its use. Organize your code as follows:
Add a second method that creates a Flask app for the specific use by Huey, similar to create_app
#__init__.py
from flask import Flask
from app.config import db, Config, huey
from app.tasks import my_task
def create_app():
# ...
return app
def create_huey_app():
app = Flask('HUEY APP')
app.config.from_object(Config)
# only initialize stuff here that is needed by Huey, eg DB connection
db.init_app(app)
# register any blueprints needed
# e.g. maybe it needs a blueprint to work with urls in email generation
return app
Make all your tasks have a first parameter that is a callable:#tasks.py
from app.config import huey
from app.models import User
# every task takes an app_factory parameter
@huey.task()
def background_task(app_factory):
app = app_factory()
with app.app_context():
User.query.get(1)
return 1
Then pass create_huey_app
as the callable to each task instantiation:#view.py
from flask import Blueprint
from app.tasks import my_task
main = Blueprint("main", __name__)
@main.route("/")
def index():
# pass the Huey app factory to the task
background_task(create_huey_app) # running the registered background task
return "hello view"
If you want to run the task locally when debugging:@main.route("/")
def index():
# pass the Huey app factory to the task
if current_app.debug:
background_task.call_local(create_huey_app) # running the task directly
else:
background_task(create_huey_app) # running the task in Huey
return "hello view"
Why flask app context is lost in child thread when application factory pattern is used?
From https://flask.palletsprojects.com/en/2.1.x/reqcontext/#lifetime-of-the-context:
When a Flask application begins handling a request, it pushes a request context, which also pushes an app context. When the request ends it pops the request context then the application context.In the case of a globalThe context is unique to each thread (or other worker type). ...
app
and db = SQLAlchemy(app)
, db
always has self.app
.In the case of application factory pattern,
db
gets the app context from the thread.Pass an AppContext
to be pushed onto the thread:
# def printer(): # Change this
def printer(app_context): # to this
app_context.push() #
print(User.query.all())
@blueprint.route('/')
def index():
# athread = Thread(target=printer, args=(), daemon=True) # Change this
athread = Thread(target=printer, args=(current_app.app_context(),), daemon=True) # to this
athread.start()
return 'ok'
How to use flask app context when using decorators
The problem is in the way of using auth_token_required decorator. auth_token_required
decorator unlike auth_required
doesn't accept any additional arguments for configuration and expects the only decorated function to be transmitted.
Your code can be fixed by applying of one of the following variants:
@article_manager.route('/<string:title>', '/<int:id>')
class GetArticle(Resource):
@auth_token_required
def get(self, title=None, id=None):
pass
or
@article_manager.route('/<string:title>', '/<int:id>')
class GetArticle(Resource):
@auth_required("token")
def get(self, title=None, id=None):
pass
Implementing threading with flask with application context
I found a solution to this issue. I added this code to my backend.py:
from flask import Blueprint
from threading import Thread
bp = Blueprint('backend', __name__)
def backend(app):
thread = Thread(target=start, args=(app,))
thread.daemon = True
thread.start()
I then added backend.backend(app)
to my create_app() function in __init__.py, right before the end of my function. This will call my backend() function from backend.py and pass the application context, and this function starts my sub-process.
Related Topics
Numpy 1.21.2 May Not Yet Support Python 3.10
Why Use Sys.Path.Append(Path) Instead of Sys.Path.Insert(1, Path)
In-Place Type Conversion of a Numpy Array
Memory Error When Using Pandas Read_Csv
Plotting Multiple Lines, in Different Colors, with Pandas Dataframe
How to Crop an Image Using Pil
Finding the Values of the Arrow Keys in Python: Why Are They Triples
Django Post_Save() Signal Implementation
Convert Categorical Data in Pandas Dataframe
What Is the Default _Hash_ in Python
Slicing of a Numpy 2D Array, or How to Extract an Mxm Submatrix from an Nxn Array (N>M)
How to Write a Generator Class
How to Check If Two Strings Are Anagrams of Each Other
How to Modify Procfile to Run Gunicorn Process in a Non-Standard Folder on Heroku