import os import psycopg2 from psycopg2.extras import RealDictCursor from contextlib import contextmanager import logging logger = logging.getLogger(__name__) @contextmanager def get_cursor(): """ New connection per call — psycopg2 is not thread-safe across Gradio workers. Commits on clean exit, rolls back on exception. """ url = os.environ.get("DATABASE_URL") if not url: raise RuntimeError("DATABASE_URL not set — is Docker Compose running?") conn = psycopg2.connect(url) try: with conn.cursor(cursor_factory=RealDictCursor) as cur: yield cur conn.commit() except Exception: conn.rollback() raise finally: conn.close()