
If you have spent some time developing Flask applications, you have probably encountered terms like request
, session
, current_app
, and g
. You might even use them daily. But have you ever stopped to think about how Flask makes these seemingly global objects available exactly when you need them, especially in a multi-threaded web server environment? Well, the magic lies in Flask's context system.
In this article, you will learn what contexts are in Flask and how to use them with practical examples.
Let’s dive in!
What Are Contexts in Flask?
In web frameworks, a context refers to a set of information relevant to the current state of processing. Think of it as a temporary workspace that holds all the tools and data needed for a specific task.
Why does Flask bother with this? Imagine a busy web server handling multiple user requests simultaneously. If objects like the current request details were truly global variables, different threads handling different requests could overwrite each other's data, leading to chaos!
Flask uses contexts to manage access to objects, such as the application instance and incoming request data, in a way that is thread-safe. Each thread gets its own version of the context, ensuring data isolation.
Flask primarily uses two types of contexts, which often work together:
- Application Context: Deals with application-level data.
- Request Context: Deals with data specific to a single incoming HTTP request.
Let's break down each one.
The Application Context: The App's Workspace
The application context is Flask's way of keeping track of application-level information that is not tied to a specific user request. Its main job is to make sure certain objects are accessible when needed, primarily the application instance itself.
When an application context is active, Flask makes available the following key objects:
current_app
: This is a proxy object that points to the Flask application instance handling the current activity. It allows your blueprints, extensions, and other modules to access the app configuration, registered routes, etc., without needing you to pass theapp
object around explicitly.g
: It stands for 'global', though its scope is context-bound. Think of it as a temporary scratchpad. It is a namespace object where you can store arbitrary data during the life of an application context. A common use case is storing a database connection or the logged-in user details once per request, making them easily accessible elsewhere in your code during that same request handling cycle. However, consider that it only holds data for the duration of a single request. So, if you need to store user data that persists between a request, you must use other objects.
Flask automatically activates an application context before handling a request and deactivates it afterward. This usually happens hand-in-hand with the request context. You can also manually push an application context using the method push()
. This is essential when you need access to current_app
or g
outside of a typical request cycle, like in background jobs, cron tasks, or Flask CLI commands.
The Request Context: Handling Individual Requests
While the application context deals with the app, the request context is all about a single incoming HTTP request from a client. It holds everything Flask needs to know to process that specific interaction.
The key objects available when a request context is active are:
request
: This is probably the most frequently used context object. It is a proxy representing the incoming HTTP request. Through it, you can access form data (request.form
), query parameters (request.args
), uploaded files (request.files
), headers (request.headers
), and much more. It is your window into what the client is asking for.session
: This object allows you to store user-specific information across multiple requests. Flask's default session implementation uses cryptographically signed cookies. When you store something insession
(e.g.,session["user_id"] = user.id
), Flask serializes it, signs it, and sends it back to the client as a cookie. On subsequent requests, Flask reads the cookie, verifies the signature, and makes the data available again via thesession
object. It is perfect for remembering who a user is between page loads.
Flask automatically pushes a request context right when it starts processing an incoming HTTP request and closes it when the response is generated and sent. Pushing a request context implicitly pushes an application context if one is not already active. This makes sense as you can not handle a request without knowing which application it belongs to. You generally do not need to manage the request context manually unless you are doing advanced testing or working with WebSockets outside the standard request-response flow.
Practical Examples
Understanding Flask contexts conceptually is one thing, but seeing them in action (and seeing things break when they're missing) really drives the point home. This section provides code examples to demonstrate:
- How
current_app
,g
,request
, andsession
work within a standard request. - What happens when you try to access these objects outside a request context.
- How to manually create contexts (
app_context
andtest_request_context
) to make these objects available when needed outside the normal request flow.
Prerequisites
To replicate this tutorial using Flask, you need to have at least Python 3.6 or newer installed on your machine.
Repository Structure and Requirements
Suppose you call the main folder of your project flask_contexts
. At the end of this step, the folder will have the following structure:
flask_contexts/ ├── venv/ ├── app.py └── app_no_context.py
Where:
venv
contains the virtual environment.app.py
contains the code that manages the contexts.app_no_context.py
contains the code that simulates a call outside the contexts.
You can create the venv
virtual environment directory like so:
python -m venv venv
To activate it on Windows, run:
venv\Scripts\activate
Or on macOS and Linux, execute:
source venv/bin/activate
In the activated virtual environment, install the dependencies with:
pip install flask
Wonderful! You now have what you need to create a Flask application using contexts.
Step 1: Basic Setup and App Initialization
As a first step, you need to set the application up in the app.py
file:
import time import secrets from flask import Flask, request, session, g, current_app, jsonify # Application Setup app = Flask(__name__) app.secret_key = secrets.token_hex(16)
In this part of the code:
app = Flask(__name__)
: Creates an instance of the Flask application, where__name__
helps Flask determine the root path for resources.app.secret_key = secrets.token_hex(16)
: Sets a secret key that generates a random 32-character hexadecimal string, suitable for a development environment.
Step 2: Define a Function That Needs Context
Define a Python function that attempts to access context-bound objects (current_app
, g
, request
, and session
). This function is designed to work correctly if called when the necessary contexts are active, but it will raise a RuntimeError
if called when they are not:
def function_potentially_outside_context(): """ Tries to access context-bound objects. This will FAIL if called outside an active context. """ try: # These require an active Application Context app_name = current_app.name g.some_value = "Set value in g" # These require an active Request Context (which includes App Context) method = request.method session_val = session.get("example", "Not Set") return (f"SUCCESS (Inside Context): App='{app_name}', Method='{method}', " f"Session='{session_val}', g.some_value='{g.some_value}'") except RuntimeError as e: # This is the typical error when accessing context objects outside a context return f"FAILED: Caught RuntimeError: {e}" except Exception as e: # Catch any other unexpected errors return f"FAILED: Caught unexpected Exception: {e}"
In this snippet, the try-except
block manages the context-sensitive operations. If any of current_app
, g
, request
, or session
are accessed when their corresponding context is not active, Flask raises a RuntimeError
.
Step 3: Set a Standard Route
Create a standard Flask route. When a user visits the root URL, Flask automatically sets up both the Application and Request contexts before executing the index
function. Inside this function, you can freely use current_app
, g
, request
, and session
.
@app.route("/") def index(): """ Standard route where Flask handles contexts automatically. Demonstrates 'g' reset and 'session' persistence. """ print("\n---> Entering index route handler") # Check if 'g' has data from a previous request g_before = getattr(g, "request_timestamp", "Not Set Before This Request") print(f"Value of g.request_timestamp before setting: {g_before}") # Set a value in 'g' for this specific request g.request_timestamp = time.time() print(f"Set g.request_timestamp: {g.request_timestamp}") # Increment a session counter visit_count = session.get("visit_count", 0) + 1 session["visit_count"] = visit_count print(f"Updated session['visit_count']: {visit_count}") # Access other context objects app_name = current_app.name req_method = request.method print(f"Accessed current_app.name: {app_name}") print(f"Accessed request.method: {req_method}") # Simulate work and use 'g' within the same request time.sleep(0.05) duration = time.time() - g.request_timestamp print(f"Request processing duration (using g): {duration:.4f}s") print("<--- Exiting index route handler") return jsonify({ "message": "Inside standard request - contexts handled automatically.", "app_name": app_name, "request_method": req_method, "g_timestamp_set_in_this_request": g.request_timestamp, "session_visit_count": visit_count, "processing_duration_seconds": f"{duration:.4f}" })
This route does the following:
getattr()
: Safely checks ifg
has the attributerequest_timestamp
from this request's context setup. This showsg
is fresh for each request.time.time()
: Stores the current time ing
. This value is only available during this specific request.session.get() + 1
: Retrievesvisit_count
from the session (defaulting to 0 if not found) and increments it at each new visit.session["visit_count"] = visit_count
: Stores the new count back in the session. This data will persist across requests for the same user.current_app.name
,request.method
: Provide direct access works because Flask sets up the contexts.jsonify()
: Converts the Python dictionary into a JSON response for the browser.
Step 4: Call the Helper Function Within a Request
Create another route that calls the helper function from Step 2. Because this call happens inside a route handler where Flask has already set up the contexts, the helper function will succeed:
@app.route("/call-function-directly") def call_function_directly(): """ Calls the helper function directly from within a request. Contexts are already active, so it should succeed. """ print("\n---> Entering call_function_directly route handler") # Since we are inside a request handler, contexts are active. result = function_potentially_outside_context() # Call the helper print(f"Result from helper: {result}") print("<--- Exiting call_function_directly route handler") return jsonify({"test": "Calling function from within a route", "result": result})
In this route, result = function_potentially_outside_context()
calls the function defined in Step 2. Since Flask ensures contexts are active here, the try-except
block inside the function succeeds.
Step 5: Simulate the "No Context" Problem
This step highlights when you encounter problems. Create a route that simulates what happens if you try to call function_potentially_outside_context
from outside Flask's request handling (like in a separate script or background task). The route itself can not truly demonstrate the failure (because it is a request), but this will be discussed later.
@app.route("/call-function-no-context") def call_function_no_context_trigger(): """ Simulates calling the function where contexts might be missing. This route primarily serves to show the function's output format. """ print("\n---> Entering call_function_no_context_trigger route handler") # Simulate the call outside of a request context result = "Simulated Failure: Imagine 'RuntimeError: Working outside of application context.' was raised here." print("<--- Exiting call_function_no_context_trigger route handler") return jsonify({ "test": "Simulating call without context (via route)", "result": result, "note": "To see real failure, run function_potentially_outside_context() from a plain script." })
This function will be invoked in app_no_context.py
next to show the actual simulation outside the contexts.
Step 6: Running the Application
To run the app.py
application, write the following:
if __name__ == "__main__": print("\nStep 8: Starting Flask development server...") print("Access the examples at:") print("- http://127.0.0.1:5000/") print("- http://127.0.0.1:5000/call-function-directly") print("- http://127.0.0.1:5000/call-function-no-context") print("\nRun 'python test_no_context.py' in another terminal to see the context error.") app.run(debug=True, host='0.0.0.0', port=5000)
Perfect! Now your app.py
file is ready to be tested!
Step 7: Manage the Request Outside the Context
To manage the request outside the context, write the following in the app_no_context.py
file:
from app import app, function_potentially_outside_context print("Attempting to call function outside Flask's request handling...") result = function_potentially_outside_context() print(f"Result: {result}")
Now you are ready to also test the app_no_context.py
file.
Step 8: Testing
As a first test, try the main application by writing:
python app.py
This is what you will see when you access the URL http://127.0.0.1:5000/
:

The browser shows that you have been successful, and each time you refresh the page, the session_visit_count
will increase, as expected. Even the timestamp will be different, as it is managed by the main route.
This is the result when you access the URL http://127.0.0.1:5000/call-function-directly
:

Because the function is called from within a route where Flask has already set up the contexts, the result is a SUCCESS message.
The interesting part is to simulate the request externally from the contexts. To do so, open a new terminal and type:
python app_no_context.py
You will obtain the following result on the CLI:

This demonstrates the necessity of a context, as the function has been called outside of it, on purpose.
Step 9: Put it All Together
Below is what the app.py
file should now contain:
import time import secrets from flask import Flask, request, session, g, current_app, jsonify # Application Setup app = Flask(__name__) app.secret_key = secrets.token_hex(16) def function_potentially_outside_context(): """ Tries to access context-bound objects. This will FAIL if called outside an active context. """ try: # These require an active Application Context app_name = current_app.name g.some_value = "Set value in g" # These require an active Request Context (which includes App Context) method = request.method session_val = session.get("example", "Not Set") return (f"SUCCESS (Inside Context): App='{app_name}', Method='{method}', " f"Session='{session_val}', g.some_value='{g.some_value}'") except RuntimeError as e: # This is the typical error when accessing context objects outside a context return f"FAILED: Caught RuntimeError: {e}" except Exception as e: # Catch any other unexpected errors return f"FAILED: Caught unexpected Exception: {e}" @app.route("/") def index(): """ Standard route where Flask handles contexts automatically. Demonstrates 'g' reset and 'session' persistence. """ print("\n---> Entering index route handler") # Check if 'g' has data from a *previous* request (it shouldn't) g_before = getattr(g, "request_timestamp", "Not Set Before This Request") print(f"Value of g.request_timestamp before setting: {g_before}") # Set a value in 'g' for this specific request g.request_timestamp = time.time() print(f"Set g.request_timestamp: {g.request_timestamp}") # Increment a session counter visit_count = session.get("visit_count", 0) + 1 session["visit_count"] = visit_count print(f"Updated session['visit_count']: {visit_count}") # Access other context objects app_name = current_app.name req_method = request.method print(f"Accessed current_app.name: {app_name}") print(f"Accessed request.method: {req_method}") # Simulate work and use 'g' within the same request time.sleep(0.05) duration = time.time() - g.request_timestamp print(f"Request processing duration (using g): {duration:.4f}s") print("<--- Exiting index route handler") return jsonify({ "message": "Inside standard request - contexts handled automatically.", "app_name": app_name, "request_method": req_method, "g_timestamp_set_in_this_request": g.request_timestamp, "session_visit_count": visit_count, "processing_duration_seconds": f"{duration:.4f}" }) @app.route("/call-function-directly") def call_function_directly(): """ Calls the helper function directly from within a request. Contexts are already active, so it should succeed. """ print("\n---> Entering call_function_directly route handler") # Since you are inside a request handler, contexts are active. result = function_potentially_outside_context() print("<--- Exiting call_function_directly route handler") return jsonify({"test": "Calling function from within a route", "result": result}) @app.route("/call-function-no-context") def call_function_no_context_trigger(): """ Simulates calling the function where contexts might be missing. This route primarily serves to show the function's output format. """ print("\n---> Entering call_function_no_context_trigger route handler") # Simulate the call outside of a request context result = "Simulated Failure: Imagine 'RuntimeError: Working outside of application context.' was raised here." print("<--- Exiting call_function_no_context_trigger route handler") return jsonify({ "test": "Simulating call without context (via route)", "result": result, "note": "To see real failure, run function_potentially_outside_context() from a plain script." }) if __name__ == "__main__": print("Starting Flask development server...") print("Access the examples at:") print("- http://127.0.0.1:5000/") print("- http://127.0.0.1:5000/call-function-directly") print("- http://127.0.0.1:5000/call-function-no-context") app.run(debug=True, host='0.0.0.0', port=5000)
Discussion About the "No Context" Simulation
The app_no_context.py
example shows that things break, but not when you could encounter it naturally, because you simulated it—somehow forcing it.
So, when can the app_no_context.py
scenario happen in real life?
The automatic context setup only happens during a live web request. There are many situations where you might want to run parts of your Flask application's code outside of a direct web request. This is where you need to manually create contexts.
Here are common real-world examples where you would be "outside" a request and need contexts:
- Background jobs/task queues: A user uploads a video (web request). Your view function adds a task to a queue (like Celery) to process the video later. While the trigger to add a task to the queue might come from an HTTP request handled by Flask, the actual execution of the task happens later in a completely separate worker process. This worker process is not the same process as the Flask web server handling requests. It simply pulls a job description from a queue (like Redis or RabbitMQ) and runs the associated Python code. Since this worker process is not directly handling an incoming HTTP request, Flask doesn't automatically set up any contexts for it.
- Custom Flask CLI commands: You write a command like
flask create-admin --email admin@example.com
to add an admin user directly from your terminal. In this case, you are executing a Python script via the Flask command-line interface runner. There is no incoming HTTP request involved. You are interacting directly with the script on the command line. Because there's no web request being processed, Flask's automatic request-response context setup mechanism does not run. - Scheduled tasks: You have a Python script run by
cron
every night to clean up old user sessions or generate reports. Similarly to CLI commands, this execution happens independently of any web server or incoming HTTP request. Since Flask is not involved in handling an HTTP request at that moment, it doesn't automatically create the contexts.
Wrapping Up
In this article, you learned that Flask's context system is a fundamental mechanism that ensures application and request data are managed correctly and safely, especially in concurrent environments. By distinguishing between the Application Context and the Request Context, Flask offers a robust way to handle state.
As demonstrated, attempting to access context-bound objects outside of an active context leads to a RuntimeError
. Recognizing when these situations arise allows developers to manually push the necessary contexts.
Mastering Flask contexts allows you to write more reliable, maintainable, and testable applications, enabling you to leverage Flask's full potential beyond simple request handling.
Happy testing!
Wondering what you can do next?
Finished this article? Here are a few more things you can do:
- Subscribe to our Python Wizardry newsletter and never miss an article again.
- Start monitoring your Python app with AppSignal.
- Share this article on social media
Most popular Python articles
An Introduction to Flask-SQLAlchemy in Python
In this article, we'll introduce SQLAlchemy and Flask-SQLAlchemy, highlighting their key features.
See moreMonitor the Performance of Your Python Flask Application with AppSignal
Let's use AppSignal to monitor and improve the performance of your Flask applications.
See moreFind and Fix N+1 Queries in Django Using AppSignal
We'll track the N+1 query problem in a Django app and fix it using AppSignal.
See more

Federico Trotta
Guest author Federico is a freelance Technical Writer who specializes in writing technical articles and documenting digital products. His mission is to democratize software through technical content.
All articles by Federico TrottaBecome our next author!
AppSignal monitors your apps
AppSignal provides insights for Ruby, Rails, Elixir, Phoenix, Node.js, Express and many other frameworks and libraries. We are located in beautiful Amsterdam. We love stroopwafels. If you do too, let us know. We might send you some!
