python

How the Application and Request Contexts Work in Python Flask

Federico Trotta

Federico Trotta on

How the Application and Request Contexts Work in Python Flask

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:

  1. Application Context: Deals with application-level data.
  2. 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 the app 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 in session (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 the session 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:

  1. How current_app, g, request, and session work within a standard request.
  2. What happens when you try to access these objects outside a request context.
  3. How to manually create contexts (app_context and test_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:

plaintext
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:

Shell
python -m venv venv

To activate it on Windows, run:

Shell
venv\Scripts\activate

Or on macOS and Linux, execute:

Shell
source venv/bin/activate

In the activated virtual environment, install the dependencies with:

Shell
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:

Python
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:

Python
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.

Python
@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 if g has the attribute request_timestamp from this request's context setup. This shows g is fresh for each request.
  • time.time(): Stores the current time in g. This value is only available during this specific request.
  • session.get() + 1: Retrieves visit_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:

Python
@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.

Python
@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:

Python
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:

Python
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:

Shell
python app.py

This is what you will see when you access the URL http://127.0.0.1:5000/:

The main URL shows the context has been used in a Flask application by Federico Trotta

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:

The manual call inside contexts in Flask by Federico Trotta

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:

Shell
python app_no_context.py

You will obtain the following result on the CLI:

A request outside contexts in Flask by Federico Trotta

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:

Python
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:

  • Share this article on social media
Federico Trotta

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 Trotta

Become our next author!

Find out more

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!

Discover AppSignal
AppSignal monitors your apps