Skip to main content

Overview

The application context keeps track of application-level data during a request or CLI command. Flask automatically creates and manages this context.

What is Application Context?

From ctx.py:260-298, the AppContext class provides:
  • Access to current_app and g proxies
  • Application configuration and resources
  • Per-request or per-command isolated state
class AppContext:
    """An app context contains information about an app, and about the request
    when handling a request. A context is pushed at the beginning of each
    request and CLI command, and popped at the end."""
    
    def __init__(self, app, *, request=None, session=None):
        self.app = app
        self.g = app.app_ctx_globals_class()
        self.url_adapter = None
        # ...

When is it Active?

Application context is automatically pushed:
  1. During requests - Automatically for each request
  2. In CLI commands - When using flask commands
  3. In tests - When using test client or context
  4. Manually - When you explicitly create one
from flask import Flask, current_app

app = Flask(__name__)

# During request - automatic
@app.route('/')
def index():
    # Context is active
    print(current_app.name)  # Works!
    return 'Hello'

# Outside request - manual
with app.app_context():
    # Context is active
    print(current_app.name)  # Works!
    db.create_all()

The current_app Proxy

Access the current application instance:
from flask import current_app

@app.route('/config')
def show_config():
    # Access app configuration
    debug = current_app.config['DEBUG']
    secret = current_app.secret_key
    
    # Access app methods
    current_app.logger.info('Showing config')
    
    # Access app attributes
    name = current_app.name
    
    return f'App: {name}, Debug: {debug}'

Why Use current_app?

Instead of importing app directly:
# Don't do this in reusable code
from myapp import app

def some_function():
    return app.config['SECRET_KEY']

# Do this instead
from flask import current_app

def some_function():
    return current_app.config['SECRET_KEY']
Benefits:
  • Works with application factories
  • Supports multiple app instances
  • Works in blueprints and extensions

The g Object

From ctx.py:30-116, g is a namespace object for storing data during a context:
from flask import g

@app.before_request
def load_user():
    # Store data in g
    if 'user_id' in session:
        g.user = User.query.get(session['user_id'])
    else:
        g.user = None

@app.route('/dashboard')
def dashboard():
    # Access data from g
    if g.user is None:
        return redirect(url_for('login'))
    
    return f'Welcome, {g.user.name}!'

@app.route('/api/data')
def api_data():
    # Check if attribute exists
    if hasattr(g, 'user'):
        return jsonify({'user': g.user.name})
    
    # Or use get() with default
    user = g.get('user', None)
    return jsonify({'user': user})

g Object Methods

From ctx.py:68-103:
# Set attribute
g.user = current_user
g.db = get_db_connection()

# Get with default
user = g.get('user', None)
db = g.get('db', create_db())

# Check existence
if 'user' in g:
    print(g.user)

# Pop value (get and remove)
db = g.pop('db', None)
if db:
    db.close()

# Set default (like dict.setdefault)
db = g.setdefault('db', create_db())

# Iterate keys
for key in g:
    print(f'{key}: {g[key]}')

Creating Application Context

Manual Context Creation

app = Flask(__name__)

# Using with statement
with app.app_context():
    # Context is active
    print(current_app.name)
    g.temp_data = 'value'
    db.create_all()
# Context is popped, g is cleaned up

# Using push/pop
ctx = app.app_context()
ctx.push()
try:
    # Do work
    print(current_app.name)
finally:
    ctx.pop()

In Tests

def test_app():
    app = create_app('testing')
    
    with app.app_context():
        # Setup
        db.create_all()
        
        # Test
        assert current_app.config['TESTING'] is True
        
        # Cleanup
        db.drop_all()

In CLI Commands

import click
from flask import current_app

@app.cli.command()
def init_db():
    """Initialize the database."""
    # Context is automatically active
    current_app.logger.info('Initializing database...')
    db.create_all()
    click.echo('Database initialized.')

Context Lifecycle

From ctx.py:416-517, understanding push and pop:
def push(self):
    """Push this context so that it is the active context."""
    self._push_count += 1
    
    if self._cv_token is not None:
        return  # Already pushed
    
    self._cv_token = _cv_app.set(self)
    appcontext_pushed.send(self.app, _async_wrapper=self.app.ensure_sync)

def pop(self, exc=None):
    """Pop this context and call teardown functions."""
    self._push_count -= 1
    
    if self._push_count > 0:
        return  # Still pushed elsewhere
    
    # Run teardown functions
    self.app.do_teardown_appcontext(self, exc)
    
    # Reset context variable
    _cv_app.reset(self._cv_token)
    self._cv_token = None
    
    appcontext_popped.send(self.app, _async_wrapper=self.app.ensure_sync)

Teardown Functions

Run cleanup code when context is popped:
@app.teardown_appcontext
def cleanup(exception=None):
    # Close database connection
    db = g.pop('db', None)
    if db is not None:
        db.close()
    
    # Clean up other resources
    cache = g.pop('cache', None)
    if cache is not None:
        cache.clear()

Error Handling in Teardown

Teardown functions receive exceptions:
@app.teardown_appcontext
def handle_db_cleanup(exception):
    db = g.pop('db', None)
    
    if db is not None:
        if exception is not None:
            # Error occurred, rollback
            db.rollback()
        else:
            # Success, commit
            db.commit()
        
        db.close()

Signals

Application context signals:
from flask import appcontext_pushed, appcontext_popped

@appcontext_pushed.connect_via(app)
def on_context_pushed(sender, **kwargs):
    print(f'Context pushed for {sender.name}')
    # Initialize resources

@appcontext_popped.connect_via(app)
def on_context_popped(sender, **kwargs):
    print(f'Context popped for {sender.name}')
    # Cleanup resources

Checking Context State

From ctx.py:235-257:
from flask import has_app_context

def get_db():
    if has_app_context():
        if 'db' not in g:
            g.db = create_database_connection()
        return g.db
    return None

@app.route('/db-info')
def db_info():
    if has_app_context():
        return 'Context active'
    return 'No context'

Common Patterns

Database Connection

from flask import g

def get_db():
    if 'db' not in g:
        g.db = connect_to_database(
            current_app.config['DATABASE_URI']
        )
    return g.db

@app.teardown_appcontext
def close_db(error):
    db = g.pop('db', None)
    if db is not None:
        db.close()

@app.route('/query')
def query():
    db = get_db()
    results = db.execute('SELECT * FROM users').fetchall()
    return jsonify(results)

Request Timer

import time
from flask import g, request

@app.before_request
def start_timer():
    g.start_time = time.time()

@app.after_request
def log_request(response):
    if hasattr(g, 'start_time'):
        elapsed = time.time() - g.start_time
        current_app.logger.info(
            f'{request.method} {request.path} - {elapsed:.3f}s'
        )
    return response

Lazy Resource Initialization

class LazyResource:
    def __init__(self, init_func):
        self.init_func = init_func
    
    def __call__(self):
        if 'lazy_resource' not in g:
            g.lazy_resource = self.init_func()
        return g.lazy_resource

# Usage
get_cache = LazyResource(lambda: Redis(current_app.config['REDIS_URL']))

@app.route('/data')
def data():
    cache = get_cache()  # Initialized only when needed
    return cache.get('data')

Application Context vs Request Context

FeatureApplication ContextRequest Context
Providescurrent_app, grequest, session
LifetimeRequest or CLI commandRequest only
CreatedAutomatically or manuallyAutomatically
Use forApp config, resourcesRequest data
From Flask 3.2+, both contexts are merged into a unified AppContext.

Best Practices

Use g for Resources

Store request-specific resources like DB connections in g

Clean Up in Teardown

Always close resources in teardown_appcontext handlers

Avoid Direct app Import

Use current_app instead of importing app directly

Check Context State

Use has_app_context() when context may not be active

Nested Contexts

Contexts can be nested:
with app1.app_context():
    print(current_app.name)  # app1
    
    with app2.app_context():
        print(current_app.name)  # app2
    
    print(current_app.name)  # app1 again

Build docs developers (and LLMs) love