The Unified Context System
In this codebase, the context system has undergone a significant architectural shift. Starting with version 3.2, the previously separate application and request contexts have been merged into a single, unified model managed by the AppContext class. This system ensures that application state, global data, and request-specific information are consistently available and safely isolated across different threads or asynchronous tasks.
The Unified AppContext
The AppContext class in src/flask/ctx.py serves as the primary container for all state during the execution of a request or a CLI command. It replaces the legacy RequestContext (which remains as a deprecated alias for backward compatibility).
An AppContext can exist in two states:
- Application Context: Contains only application-level information (
appandg). This is typically used for CLI commands or background tasks. - Request Context: Contains application-level information plus request-specific data (
requestandsession). This is automatically created and pushed during an HTTP request.
The class signature reflects this dual role:
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 | None = request
self._session: SessionMixin | None = session
# ...
Context Proxies
To provide convenient access to the active context's data without passing the context object around, the codebase uses LocalProxy objects defined in src/flask/globals.py. These proxies point to the current AppContext instance stored in a contextvars.ContextVar.
current_app: Points toapp_ctx.app. It represents the active Flask application.g: Points toapp_ctx.g. This is an instance of_AppCtxGlobalsused for storing temporary data during the context's lifetime.request: Points toapp_ctx.request. Only available if the context was created with request data.session: Points toapp_ctx.session. Likerequest, it requires a request-aware context.
The implementation in src/flask/globals.py shows how these proxies are bound to the same underlying context variable:
_cv_app: ContextVar[AppContext] = ContextVar("flask.app_ctx")
app_ctx: AppContextProxy = LocalProxy(_cv_app)
current_app: FlaskProxy = LocalProxy(_cv_app, "app")
g: _AppCtxGlobalsProxy = LocalProxy(_cv_app, "g")
request: RequestProxy = LocalProxy(_cv_app, "request")
session: SessionMixinProxy = LocalProxy(_cv_app, "session")
Lifecycle Management
The lifecycle of a context is managed through push() and pop() methods. When a context is pushed, it becomes the active context for the current execution thread or task.
Automatic Management
In a standard web request, the Flask.wsgi_app method (in src/flask/app.py) handles the context lifecycle automatically:
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()
Manual Management
For tasks outside of the standard request loop, such as CLI commands or unit tests, you can manually manage the context using the with statement. The Flask class provides helper methods for this:
app.app_context(): Creates a context without request data.app.test_request_context(): Creates a context with simulated request data.
Example from tests/test_appctx.py:
with app.app_context():
# current_app and g are available here
rv = flask.url_for("index")
assert rv == "https://localhost/"
Global Data with g
The g object (an instance of _AppCtxGlobals in src/flask/ctx.py) is a namespace for storing data during a single context. It is often used to store database connections or authenticated user information that needs to be accessed across different functions.
_AppCtxGlobals supports attribute access as well as dictionary-like methods:
with app.app_context():
g.db_connection = connect_db()
# Later in the same context
db = g.get("db_connection")
Teardown and Cleanup
When AppContext.pop() is called, the system performs cleanup by executing teardown functions registered with the application. This happens even if an unhandled exception occurred during the context's lifetime.
The pop() method in src/flask/ctx.py ensures that both request-level and application-level teardowns are executed:
app.do_teardown_request(self, exc): Runs functions registered with@app.teardown_request.self._request.close(): Closes the request object.app.do_teardown_appcontext(self, exc): Runs functions registered with@app.teardown_appcontext.
If multiple errors occur during these teardown steps, the codebase uses a _CollectErrors helper to gather them and eventually raises an ExceptionGroup (on Python 3.11+) or a combined error, ensuring that one failure doesn't prevent other cleanup tasks from running.
Migration from RequestContext
Because RequestContext has been merged into AppContext, any code explicitly referencing flask.ctx.RequestContext or the request_ctx proxy will trigger a DeprecationWarning.
In src/flask/globals.py, the request_ctx attribute is handled dynamically to facilitate this transition:
def __getattr__(name: str) -> t.Any:
if name == "request_ctx":
warnings.warn(
"'request_ctx' has merged with 'app_ctx', and will be removed"
" in Flask 4.0. Use 'app_ctx' instead.",
DeprecationWarning,
stacklevel=2,
)
return app_ctx
raise AttributeError(name)
Developers should update their code to use app_ctx or the specific data proxies (request, session) directly.