Flask implementa un sistema de gestión de contextos para aislar los datos específicos de cada solicitud y del entorno de la aplicación. Este diseño evita colisiones entre peticiones concurrentes y permite un acceso seguro a objetos como request, session y g.
Mecanismos de Soporte Subyacentes
Antes de analizar el contexto, es útil comprender dos patrones clave utilizados internamente:
- Funciones parciales: permiten fijar argumentos predefinidos para simplificar llamadas posteriores.
- Personalización de atributos con
__setattr__: controla cómo se asignan valores a instancias, útil para inyección dinámica o validación.
Ejemplo práctico de función parcial:
from functools import partial
def compute_sum(a, b, c):
return a + b + c
# Fijamos 'a' como 10 para reutilizar fácilmente
sum_with_ten = partial(compute_sum, 10)
print(sum_with_ten(2, 3)) # Resultado: 15
Ejemplo de control de asignación de atributos:
class DataContainer:
def __init__(self):
object.__setattr__(self, "_data", {})
def __setattr__(self, name, value):
if name.startswith("_"):
super().__setattr__(name, value)
else:
self._data[name] = value
container = DataContainer()
container.user_id = 42
print(container._data) # {'user_id': 42}
Flujo de Ejecución del Contexto de Solicitud
Cuando una petición HTTP llega al servidor:
- Werkzeug invoca
app(environ, start_response), que desencadenaFlask.__call__(). - Este método crea una instancia de
RequestContext, pasando el entorno WSGI (environ) y la aplicación. - Dentro de
RequestContext, se inicializan objetos comorequest(envoltura del entorno) ysession(inicialmenteNone). - Al llamar a
ctx.push(), el contexto se almacena en una pila gestionada por_request_ctx_stack, una instancia deLocalStack.
La clase LocalStack utiliza Local —una estructura que mantiene datos aislados por hilo o corutina— mediante un almacenamiento interno indexado por identificadores únicos (get_ident). Esto garantiza que cada petición acceda solo a sus propios datos.
Acceso a Objetos desde las Vistas
Aunque es posible acceder directamente al contexto mediante la pila global:
from flask import Flask
from flask.globals import _request_ctx_stack
app = Flask(__name__)
@app.route('/')
def home():
ctx = _request_ctx_stack.top
method = ctx.request.method if ctx else 'UNKNOWN'
return f"Método HTTP: {method}"
Flask proporciona una interfaz más limpia mediante LocalProxy. Por ejemplo, el objeto request es una instancia de LocalProxy que envuelve una función parcial capaz de resolver dinámicamente top.request en tiempo de ejecución.
Cuando se accede a request.url, se invoca __getattr__ en LocalProxy, que delega a _get_current_object(), obteniendo así el valor real desde el contexto actual sin requerir referencias explícitas.
Implementación del Objeto session
El objeto session se inicializa como None dentro del contexto. Tras la invocación de push(), Flask:
- Extrae y descifra la cookie
sessiondel encabezado HTTP. - Asigna los datos deserializados al atributo
ctx.session. - Al finalizar la respuesta, serializa y cifra nuevamente el contenido de
session, estableciéndolo como cookie en la respuesta.
Esto permite persistencia entre peticiones sin almacenar datos sensibles en el cliente, siempre que se use una clave secreta válida.
Contexto de Aplicación vs. Contexto de Solicitud
Flask distingue dos niveles de contexto:
- Contexto de soliictud: activo durante la vida útil de una petición HTTP; contiene
request,sessionyg. - Contexto de aplicación: activo durante operaciones fuera del ciclo HTTP (por ejemplo, CLI, tareas en segundo plano); contiene
current_appyga nivel de app.
Ambos usan instancias separadas de Local, lo que previene interferencias entre distintos tipos de operaciones.
Objeto g: Almacenamiento Temopral por Petición
El objeto g (de global) es un contenedor mutable disponible durante toda la duración del contexto actual. Sus características clave son:
- Ámbito temporal: existe solo mientras dure el contetxo (solicitud o aplicación).
- No persistente: no se transfiere entre redirecciones ni peticiones distintas.
- Diferencia con variables globales: estas residen en memoria del proceso y son compartidas por todas las peticiones, mientras que
gestá aislado por contexto.
Se usa comúnmente en decoradores @before_request para adjuntar datos útiles (como usuarios autenticados o configuraciones específicas) que luego consumen las vistas.