Skip to main content

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: The Flask application instance.
  • g: An instance of _AppCtxGlobals, used for temporary data storage during the context's lifetime.
  • request: The Request object (if the context represents a request).
  • session: The Session object (if the context represents a request).
  • url_adapter: A Werkzeug MapAdapter used 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:

  1. Request Teardown: If the context has request data, it calls functions registered with @app.teardown_request.
  2. 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