Spaces:
Runtime error
Runtime error
| """ | |
| Archivo: session.py | |
| Fecha de modificación: 03/06/2026 | |
| Autor: Equipo AgroVisión | |
| Descripción: | |
| Fábrica de motor y sesiones async de SQLAlchemy contra el Supabase del usuario | |
| (PostgreSQL + PostGIS). Construye la URL async (driver asyncpg) a partir de | |
| `DATABASE_URL` descomponiéndola con `URL.create` para tolerar contraseñas con | |
| caracteres especiales. Está pensado para el **Session pooler** de Supabase | |
| (IPv4-compatible); `statement_cache_size=0` evita choques de prepared statements | |
| con pgbouncer. | |
| Acciones Principales: | |
| - Expone `get_engine()` y `get_sessionmaker()` (memoizados). | |
| Estructura Interna: | |
| - `_async_url`: deriva la URL `postgresql+asyncpg` desde `DATABASE_URL`. | |
| - `get_engine` / `get_sessionmaker`: singletons por proceso. | |
| Entradas / Dependencias: | |
| - `sqlalchemy[asyncio]`, `asyncpg`; variable de entorno `DATABASE_URL`. | |
| Salidas / Efectos: | |
| - Abre conexiones a la BD del usuario; no persiste credenciales. | |
| Ejemplo de Integración: | |
| from backend.db.session import get_sessionmaker | |
| async with get_sessionmaker()() as session: | |
| ... | |
| """ | |
| from __future__ import annotations | |
| import os | |
| from functools import lru_cache | |
| from urllib.parse import unquote, urlparse | |
| from sqlalchemy import URL | |
| from sqlalchemy.ext.asyncio import AsyncEngine, async_sessionmaker, create_async_engine | |
| def _async_url() -> URL: | |
| """ | |
| Construye la URL async de SQLAlchemy a partir de `DATABASE_URL`. | |
| Returns: | |
| URL: URL con driver `postgresql+asyncpg` y componentes desescapados. | |
| """ | |
| parsed = urlparse(os.environ["DATABASE_URL"]) | |
| return URL.create( | |
| "postgresql+asyncpg", | |
| username=unquote(parsed.username) if parsed.username else None, | |
| password=unquote(parsed.password) if parsed.password else None, | |
| host=parsed.hostname, | |
| port=parsed.port, | |
| database=parsed.path.lstrip("/") or None, | |
| ) | |
| def get_engine() -> AsyncEngine: | |
| """Crea (una vez) el motor async; `statement_cache_size=0` por el pooler pgbouncer.""" | |
| return create_async_engine( | |
| _async_url(), | |
| pool_pre_ping=True, | |
| connect_args={"statement_cache_size": 0}, | |
| ) | |
| def get_sessionmaker() -> async_sessionmaker: | |
| """Devuelve (una vez) la fábrica de sesiones async.""" | |
| return async_sessionmaker(get_engine(), expire_on_commit=False) | |