The Unified Context System
The context system in Flask manages application-level and request-level state, ensuring that data like the current application instance, the request object, and the session are accessible globally within a specific execution thread or task. In Flask 3.2, this system was unified into a single AppContext class, merging what were previously separate application and request contexts.
The Unified AppContext
The AppContext class in src/flask/ctx.py serves as the central container for all state during a request or a CLI command. It holds references to the core components of the Flask environment:
app: TheFlaskapplication instance.g: An instance of_AppCtxGlobals, used for temporary data storage during the context's lifetime.request: TheRequestobject (if the context represents a request).session: TheSessionobject (if the context represents a request).url_adapter: A WerkzeugMapAdapterused for URL matching and generation.
The context is designed to be "request-aware." As noted in the AppContext docstring, it is referred to as a "request context" if it contains request information, and an "app context" if it does not.
# src/flask/ctx.py
class AppContext:
def __init__(
self,
app: Flask,
*,
request: Request | None = None,
session: SessionMixin | None = None,
) -> None:
self.app = app
self.g: _AppCtxGlobals = app.app_ctx_globals_class()
self._request = request
self._session = session
# ...
Context Lifecycle: Push and Pop
The context system operates on a stack-like mechanism managed by contextvars. For a context to be "active," it must be pushed.
Automatic Management
In a standard web request, the Flask.wsgi_app method in src/flask/app.py handles the lifecycle automatically. It creates a context from the WSGI environment, pushes it before dispatching the request, and pops it after the response is generated.
# src/flask/app.py
def wsgi_app(self, environ: WSGIEnvironment, start_response: StartResponse):
ctx = self.request_context(environ)
try:
ctx.push()
response = self.full_dispatch_request(ctx)
return response(environ, start_response)
finally:
ctx.pop(error)
Manual Management
Developers can manually manage contexts using Python's with statement. This is common in CLI commands, background tasks, or unit tests where a request isn't automatically triggered by a web server.
app.app_context(): Creates a context without request data.app.test_request_context(): Creates a context with simulated request data for testing.
# Example of manual context usage
with app.app_context():
# current_app and g are now available
print(current_app.name)
with app.test_request_context('/login', method='POST'):
# request and session are now available
print(request.path)
Global Proxies
Flask provides several global proxies in flask.globals (such as current_app, request, g, and session) that point to the attributes of the currently active AppContext. These proxies use contextvars to ensure that even in multi-threaded or asynchronous environments, each execution context sees its own state.
When you access request.method, the proxy internally looks up the active AppContext via _cv_app.get() and returns its request attribute. If no context is pushed, or if a context without request data is active, accessing request will raise a RuntimeError.
# src/flask/ctx.py
@property
def request(self) -> Request:
if self._request is None:
raise RuntimeError("There is no request in this context.")
return self._request
Teardown and Resource Cleanup
When an AppContext is popped, Flask triggers cleanup logic. This is where resources like database connections or file handles should be closed. The pop() method coordinates two levels of teardown:
- Request Teardown: If the context has request data, it calls functions registered with
@app.teardown_request. - App Context Teardown: It calls functions registered with
@app.teardown_appcontext.
The pop() method ensures that even if one teardown function fails, others are still executed. In Flask 3.2+, multiple errors during teardown are collected and raised together (as an ExceptionGroup in Python 3.11+).
# src/flask/ctx.py
def pop(self, exc: BaseException | None = None) -> None:
# ... (validation and push_count checks)
if self._request is not None:
with collect_errors:
self.app.do_teardown_request(self, exc)
with collect_errors:
self._request.close()
with collect_errors:
self.app.do_teardown_appcontext(self, exc)
# ...
Nested Contexts
The AppContext tracks a _push_count. This allows the same context instance to be pushed multiple times (e.g., during nested function calls or complex testing scenarios). The context only performs its final cleanup and signals (appcontext_popped) when the _push_count returns to zero, ensuring that resources remain available until the outermost block finishes.
# src/flask/ctx.py
def push(self) -> None:
self._push_count += 1
if self._cv_token is not None:
return # Already pushed
# ... perform initial push logic