Contexts and Proxies
In this project, contexts and proxies are the mechanisms used to manage the lifecycle of application and request data. They allow developers to access global-like variables—such as the current application instance or the active request—without explicitly passing them through every function call.
The Unified Application Context
Historically, Flask maintained separate application and request contexts. In this version (3.2+), these have been unified into a single AppContext class found in src/flask/ctx.py. This class manages the state for both application-level operations (like CLI commands) and HTTP request handling.
The AppContext holds references to:
self.app: TheFlaskapplication instance.self.g: An instance of_AppCtxGlobals, a namespace for temporary data.self._request: TheRequestobject (if the context was created for a request).self._session: The session data (loaded lazily on first access).
When a request begins or a CLI command starts, an AppContext is "pushed" onto a stack managed by contextvars. This makes the context active for the current execution thread or task.
Context Proxies
To provide easy access to the active context's data, the project uses LocalProxy objects defined in src/flask/globals.py. These proxies forward all operations to the object currently bound to the active context.
The primary proxies are:
current_app: Points to theFlaskinstance handling the current context.g: Points to the_AppCtxGlobalsobject for the current context.request: Points to theRequestobject (available only if the context has request data).session: Points to the session dictionary (available only if the context has request data).
Using proxies instead of direct object references is critical for modularity. For example, a blueprint or a database utility doesn't need to know which specific app instance is running; it simply uses current_app to access configuration.
Per-Context Storage with g
The g object (an instance of _AppCtxGlobals in src/flask/ctx.py) is a simple namespace for storing data that should persist for the duration of a single context but not across different requests.
A common pattern in this codebase is using g to manage database connections. This ensures that a connection is created only when needed and is shared across all functions during a single request.
As seen in examples/tutorial/flaskr/db.py:
def get_db():
if "db" not in g:
g.db = sqlite3.connect(
current_app.config["DATABASE"], detect_types=sqlite3.PARSE_DECLTYPES
)
g.db.row_factory = sqlite3.Row
return g.db
Lifecycle and Teardown
The lifecycle of a context is managed through push() and pop() methods. When a context is popped, the project executes "teardown" functions. These are essential for cleaning up resources like database connections or file handles.
The AppContext.pop() method in src/flask/ctx.py handles this cleanup:
def pop(self, exc: BaseException | None = None) -> None:
# ... (validation logic)
if self._request is not None:
with collect_errors:
self.app.do_teardown_request(self, exc)
with collect_errors:
self.app.do_teardown_appcontext(self, exc)
# ...
Developers register these cleanup functions using decorators like @app.teardown_appcontext. In the tutorial's database implementation (examples/tutorial/flaskr/db.py), the connection is closed automatically when the context ends:
def close_db(e=None):
db = g.pop("db", None)
if db is not None:
db.close()
def init_app(app):
app.teardown_appcontext(close_db)
Manual Context Management
While Flask automatically manages contexts during HTTP requests, there are scenarios—such as unit tests or CLI commands—where a context must be created manually. The app.app_context() method returns a context manager that handles the push and pop cycle.
with app.app_context():
# current_app and g are now available
db = get_db()
init_db()
Context Persistence in Background Tasks
Because contexts are tied to the current execution state (via contextvars), they are not automatically shared with new threads or background tasks. To solve this, the project provides copy_current_request_context in src/flask/ctx.py.
This decorator captures the current context and ensures it is pushed when the decorated function is executed in a different thread. This is demonstrated in tests/test_reqctx.py:
@app.route("/")
def index():
flask.session["fizz"] = "buzz"
@flask.copy_current_request_context
def work(n: int) -> int:
# These assertions pass because the context was copied
assert flask.current_app == app
assert flask.request.path == "/"
assert flask.session["fizz"] == "buzz"
return n
result = executor.map(work, range(10))
return "Hello World!"
Similarly, stream_with_context in src/flask/helpers.py allows a generator used in a streaming response to maintain access to the context even after the initial view function has returned.
Design Tradeoffs and Constraints
The use of global proxies like current_app and request simplifies API design by reducing boilerplate, but it introduces a dependency on an active context. Accessing these proxies outside of a context will raise a RuntimeError with a descriptive message (defined in src/flask/globals.py as _no_app_msg or _no_req_msg).
Furthermore, the merger of RequestContext into AppContext in version 3.2 reflects a shift towards a simpler, unified model, though it requires developers to be mindful of DeprecationWarning if they still reference the old RequestContext or request_ctx names.