×

👋 Hello, Anthropic team!

Thanks for checking out my blog. I'm excited about the opportunity to work with you on building safe, beneficial AI systems.

Feel free to explore the posts on AI alignment, verification theory, and software engineering.

— James

Today I Learned

Flask's `g` - The "Global" That Isn't

Today I learned why Flask's request context object is called g, and it's too clever by half.

I was reviewing a Flask codebase and kept seeing this pattern:

from flask import g

def get_db():
    if "db" not in g:
        g.db = sqlite3.connect("app.db")
    return g.db

I always assumed g was just an arbitrary short name, but it turns out it stands for "global" - with a twist. It's intentionally ironic: g provides global-like access but is actually request-local. Each request gets its own isolated g instance.

The Problem It Solves

Without g, you might be tempted to use actual globals:

# DON'T DO THIS - Shared between ALL requests!
db_connection = None

@app.route("/users/<user_id>")
def get_user(user_id):
    global db_connection
    if not db_connection:
        db_connection = sqlite3.connect("app.db")
    # Race conditions! Data leaks! Connection errors!

This is a disaster waiting to happen. Multiple concurrent requests would compete for the same connection, potentially mixing user data or causing crashes.

How g Actually Works

Flask's g gives you what feels like a global variable but is actually isolated per request:

@app.route("/users/<user_id>")
def get_user(user_id):
    db = get_db()  # Gets THIS request's connection
    # Safe, isolated, no interference with other requests

The lifecycle:

  1. Request arrives → Flask creates a fresh g object
  2. You store stuff in it → g.db = connection
  3. Access it anywhere in that request → return g.db
  4. Request ends → g is destroyed, cleanup runs

The Name Is Part of the Lesson

Armin Ronacher (Flask's creator) chose g deliberately. It's a subtle joke about what developers think they want (global state) versus what they actually need (request-scoped state). The single letter makes it:

  • Quick to type (you'll use it constantly)
  • Easy to remember ("g for global")
  • Obviously special (like e for exception)

Practical Example

Here's a real pattern I now will use for managing multiple resources:

from flask import g

def get_redis():
    if "redis" not in g:
        g.redis = redis.StrictRedis()
    return g.redis

def get_user():
    if "user" not in g:
        g.user = load_user_from_token()
    return g.user

@app.teardown_appcontext
def cleanup(error):
    # Always runs, even if request fails
    if hasattr(g, "redis"):
        g.redis.close()

The Key Insight

g is "global" in the sense that it's globally accessible within your request handling code, but it's actually providing thread-safe, request-isolated storage.