"""Datenbankzugang fuer Phase 2. Eine Stelle, die DATABASE_URL aufloest - aus der Umgebung oder aus .env daneben. Zugangsdaten stehen nie im Code und nie in einem Argument, das in der Prozessliste landet. """ import os import pathlib _ENV = pathlib.Path(__file__).with_name(".env") def _aus_env_datei(): """Minimaler .env-Leser. Kein Shell-Parsing, keine Ersetzungen.""" werte = {} if not _ENV.exists(): return werte for zeile in _ENV.read_text(encoding="utf-8").splitlines(): zeile = zeile.strip() if not zeile or zeile.startswith("#") or "=" not in zeile: continue schluessel, _, wert = zeile.partition("=") werte[schluessel.strip()] = wert.strip().strip('"').strip("'") return werte def dsn(): url = os.environ.get("DATABASE_URL") or _aus_env_datei().get("DATABASE_URL") if not url: raise RuntimeError( "DATABASE_URL fehlt. .env.example nach .env kopieren und ausfuellen." ) return url def verbindung(**kwargs): """Verbindung mit Autocommit aus. Aufrufer entscheidet ueber Transaktionen.""" import psycopg kwargs.setdefault("connect_timeout", 5) return psycopg.connect(dsn(), **kwargs)