I've been using the request/application context for some time without fully understanding how it works or why it was designed the way it was. What is the purpose of the "stack" when it comes to the request or application context? Are these two separate stacks, or are they both part of one stack? Is the request context pushed onto a stack, or is it a stack itself? Am I able to push/pop multiple contexts on top of eachother? If so, why would I want to do that?
Sorry for all the questions, but I'm still confused after reading the documentation for Request Context and Application Context.
Previous answers already give a nice overview of what goes on in the background of Flask during a request. If you haven't read it yet I recommend @MarkHildreth's answer prior to reading this. In short, a new context (thread) is created for each http request, which is why it's necessary to have a thread
Local
facility that allows objects such asrequest
andg
to be accessible globally across threads, while maintaining their request specific context. Furthermore, while processing an http request Flask can emulate additional requests from within, hence the necessity to store their respective context on a stack. Also, Flask allows multiple wsgi applications to run along each other within a single process, and more than one can be called to action during a request (each request creates a new application context), hence the need for a context stack for applications. That's a summary of what was covered in previous answers.My goal now is to complement our current understanding by explaining how Flask and Werkzeug do what they do with these context locals. I simplified the code to enhance the understanding of its logic, but if you get this, you should be able to easily grasp most of what's in the actual source (
werkzeug.local
andflask.globals
).Let's first understand how Werkzeug implements thread Locals.
Local
When an http request comes in, it is processed within the context of a single thread. As an alternative mean to spawn a new context during an http request, Werkzeug also allows the use of greenlets (a sort of lighter "micro-threads") instead of normal threads. If you don't have greenlets installed it will revert to using threads instead. Each of these threads (or greenlets) are identifiable by a unique id, which you can retrieve with the module's
get_ident()
function. That function is the starting point to the magic behind havingrequest
,current_app
,url_for
,g
, and other such context-bound global objects.Now that we have our identity function we can know which thread we're on at any given time and we can create what's called a thread
Local
, a contextual object that can be accessed globally, but when you access its attributes they resolve to their value for that specific thread. e.g.Both values are present on the globally accessible
Local
object at the same time, but accessinglocal.first_name
within the context of thread 1 will give you'John'
, whereas it will return'Debbie'
on thread 2.How is that possible? Let's look at some (simplified) code:
From the code above we can see that the magic boils down to
get_ident()
which identifies the current greenlet or thread. TheLocal
storage then just uses that as a key to store any data contextual to the current thread.You can have multiple
Local
objects per process andrequest
,g
,current_app
and others could simply have been created like that. But that's not how it's done in Flask in which these are not technicallyLocal
objects, but more accuratelyLocalProxy
objects. What's aLocalProxy
?LocalProxy
A LocalProxy is an object that queries a
Local
to find another object of interest (i.e. the object it proxies to). Let's take a look to understand:Now to create globally accessible proxies you would do
and now some time early over the course of a request you would store some objects inside the local that the previously created proxies can access, no matter which thread we're on
The advantage of using
LocalProxy
as globally accessible objects rather than making themLocals
themselves is that it simplifies their management. You only just need a singleLocal
object to create many globally accessible proxies. At the end of the request, during cleanup, you simply release the oneLocal
(i.e. you pop the context_id from its storage) and don't bother with the proxies, they're still globally accessible and still defer to the oneLocal
to find their object of interest for subsequent http requests.To simplify the creation of a
LocalProxy
when we already have aLocal
, Werkzeug implements theLocal.__call__()
magic method as follows:However, if you look in the Flask source (flask.globals) that's still not how
request
,g
,current_app
andsession
are created. As we've established, Flask can spawn multiple "fake" requests (from a single true http request) and in the process also push multiple application contexts. This isn't a common use-case, but it's a capability of the framework. Since these "concurrent" requests and apps are still limited to run with only one having the "focus" at any time, it makes sense to use a stack for their respective context. Whenever a new request is spawned or one of the applications is called, they push their context at the top of their respective stack. Flask usesLocalStack
objects for this purpose. When they conclude their business they pop the context out of the stack.LocalStack
This is what a
LocalStack
looks like (again the code is simplified to facilitate understanding of its logic).Note from the above that a
LocalStack
is a stack stored in a local, not a bunch of locals stored on a stack. This implies that although the stack is globally accessible it's a different stack in each thread.Flask doesn't have its
request
,current_app
,g
, andsession
objects resolving directly to aLocalStack
, it rather usesLocalProxy
objects that wrap a lookup function (instead of aLocal
object) that will find the underlying object from theLocalStack
:All these are declared at application start-up, but do not actually resolve to anything until a request context or application context is pushed to their respective stack.
If you're curious to see how a context is actually inserted in the stack (and subsequently popped out), look in
flask.app.Flask.wsgi_app()
which is the point of entry of the wsgi app (i.e. what the web server calls and pass the http environment to when a request comes in), and follow the creation of theRequestContext
object all through its subsequentpush()
into_request_ctx_stack
. Once pushed at the top of the stack, it's accessible via_request_ctx_stack.top
. Here's some abbreviated code to demonstrate the flow:So you start an app and make it available to the WSGI server...
Later an http request comes in and the WSGI server calls the app with the usual params...
This is roughly what happens in the app...
and this is roughly what happens with RequestContext...
Say a request has finished initializing, the lookup for
request.path
from one of your view functions would therefore go as follow:LocalProxy
objectrequest
._find_request()
(the function it registered as itsself.local
).LocalStack
object_request_ctx_stack
for the top context on the stack.LocalStack
object first queries its innerLocal
attribute (self.local
) for thestack
property that was previously stored there.stack
it gets the top contexttop.request
is thus resolved as the underlying object of interest.path
attributeSo we've seen how
Local
,LocalProxy
, andLocalStack
work, now think for a moment of the implications and nuances in retrieving thepath
from:request
object that would be a simple globally accessible object.request
object that would be a local.request
object stored as an attribute of a local.request
object that is a proxy to an object stored in a local.request
object stored on a stack, that is in turn stored in a local.request
object that is a proxy to an object on a stack stored in a local. <- this is what Flask does.Little addition @Mark Hildreth's answer.
Context stack look like
{thread.get_ident(): []}
, where[]
called "stack" because used onlyappend
(push
),pop
and[-1]
(__getitem__(-1)
) operations. So context stack will keep actual data for thread or greenlet thread.current_app
,g
,request
,session
and etc isLocalProxy
object which just overrided special methods__getattr__
,__getitem__
,__call__
,__eq__
and etc. and return value from context stack top ([-1]
) by argument name (current_app
,request
for example).LocalProxy
needed to import this objects once and they will not miss actuality. So better just importrequest
where ever you are in code instead play with sending request argument down to you functions and methods. You can easy write own extensions with it, but do not forget that frivolous usage can make code more difficult for understanding.Spend time to understand https://github.com/mitsuhiko/werkzeug/blob/master/werkzeug/local.py.
So how populated both stacks? On request
Flask
:request_context
by environment (initmap_adapter
, match path)request_context
app_context
if it missed and pushed to application context stackLets take one example , suppose you want to set a usercontext (using flask construct of Local and LocalProxy).
Define one User class :
define a function to retrive user object inside current thread or greenlet
Now define a LocalProxy
Now to get userid of user in current thread usercontext.userid
explanation :
1.Local has a dict of identity and objet , identity is threadid or greenlet id , in this example _local.user = User() is eqivalent to _local.___storage__[current thread's id] ["user"] = User()
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:
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...
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 callrequest.path
, get thepath
attribute from therequest
object of the CURRENT request." Two different requests will have a different results forrequest.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 as
request.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:
Like our
request
example, theurl_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 sourl_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:
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.
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).
A "request context" is one item of the "request context stack". Similarly with the "app context" and "app context stack".
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.
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...
Then they use the
app
anddb
values in a script that should be run from the shell. For example, a "setup_tables.py" script...In this case, the Flask-SQLAlchemy extension knows about the
app
application, but duringcreate_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 thecreate_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...
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: