From 2f52d2c8464432d5f413488b234fe065824dab76 Mon Sep 17 00:00:00 2001 From: carsten Date: Tue, 29 Sep 2026 12:15:36 +0200 Subject: [PATCH] =?UTF-8?q?Ret=20alembic-crash=20n=C3=A5r=20DATABASE=5FURL?= =?UTF-8?q?=20indeholder=20et=20bogstaveligt=20"%"?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit migrations/env.py brugte config.set_main_option() til at dele databasens URL med alembic.ini — det kalder internt Pythons configparser.set(), som fortolker "%" som sin egen interpolations-syntaks og fejler på ethvert kodeord der indeholder et procenttegn (fx fra en almindelig password-generator, eller percent-encoded specialtegn i en URL). Fundet under produktionsopsætning: brugerens Postgres-kodeord indeholdt "%", hvilket crashede `alembic upgrade head` med "ValueError: invalid interpolation syntax". URL'en gemmes nu i config.attributes (en almindelig dict, ingen configparser-fortolkning involveret) i stedet, og engine'en i run_migrations_online bygges direkte med create_engine() i stedet for engine_from_config() (som ellers ville læse URL'en tilbage via configparser igen). Verificeret lokalt med både et normalt DATABASE_URL og et med et bogstaveligt "%" i kodeordet. Co-Authored-By: Claude Sonnet 5 --- migrations/env.py | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/migrations/env.py b/migrations/env.py index f788062..8b48389 100644 --- a/migrations/env.py +++ b/migrations/env.py @@ -2,7 +2,7 @@ import os import sys from logging.config import fileConfig -from sqlalchemy import engine_from_config +from sqlalchemy import create_engine from sqlalchemy import pool from sqlmodel import SQLModel @@ -18,8 +18,11 @@ from app import models # noqa: E402,F401 -- populates SQLModel.metadata via au config = context.config # Share one source of truth for the DB URL with the FastAPI app instead of -# whatever placeholder sits in alembic.ini. -config.set_main_option("sqlalchemy.url", get_settings().database_url) +# whatever placeholder sits in alembic.ini. Stored in `config.attributes` +# (a plain dict) rather than via set_main_option — that goes through +# configparser's interpolation, which crashes on a literal "%" in the URL +# (eg. from a percent-encoded special character in the password). +config.attributes["sqlalchemy_url"] = get_settings().database_url # Interpret the config file for Python logging. # This line sets up loggers basically. @@ -48,7 +51,7 @@ def run_migrations_offline() -> None: script output. """ - url = config.get_main_option("sqlalchemy.url") + url = config.attributes["sqlalchemy_url"] context.configure( url=url, target_metadata=target_metadata, @@ -67,11 +70,7 @@ def run_migrations_online() -> None: and associate a connection with the context. """ - connectable = engine_from_config( - config.get_section(config.config_ini_section, {}), - prefix="sqlalchemy.", - poolclass=pool.NullPool, - ) + connectable = create_engine(config.attributes["sqlalchemy_url"], poolclass=pool.NullPool) with connectable.connect() as connection: context.configure(