ProvenPin Install

SQLAlchemy 2 + Alembic programmatic migrations: working versions (tested 2026-10-15)

Alembic migrations driven from Python (upgrade, downgrade, autogenerate check) against SQLite, with SQLAlchemy 2 models.

passed2 passed · 0 failed2026-10-15 11:37 UTC
Status
passed
Last passed
2026-10-15 11:37 UTC
Tests
2 / 2
Language
Python
Runtime
Python 3.13.16
pinned versions3
sqlalchemy@2.1.4alembic@1.20.0pytest@9.1.1
run it
uv venv -q .venv && uv pip install -q --python .venv/bin/python -r requirements.txt
.venv/bin/python -m pytest -q
test environment
Python 3.13.16
linux 6.17.0-1022-azure
x64
runner: github-actions 38728226240

The code

Exactly the files that passed. Your agent can pull them with get_recipe("sqlalchemy-alembic-migrations") (install), or see them on GitHub.

requirements.txt
sqlalchemy==2.1.4
alembic==1.20.0
pytest==9.1.1
models.py
from sqlalchemy import String
from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column

class Base(DeclarativeBase):
    pass

class Account(Base):
    __tablename__ = "account"
    id: Mapped[int] = mapped_column(primary_key=True)
    email: Mapped[str] = mapped_column(String(255), unique=True)
migrations/env.py
from alembic import context
from sqlalchemy import engine_from_config, pool
from models import Base

config = context.config
connectable = engine_from_config(config.get_section(config.config_ini_section, {}), prefix="sqlalchemy.", poolclass=pool.NullPool)
with connectable.connect() as connection:
    context.configure(connection=connection, target_metadata=Base.metadata, render_as_batch=True)
    with context.begin_transaction():
        context.run_migrations()
migrations/script.py.mako
"""${message}"""
revision = ${repr(up_revision)}
down_revision = ${repr(down_revision)}
from alembic import op
import sqlalchemy as sa
def upgrade():
    ${upgrades if upgrades else "pass"}
def downgrade():
    ${downgrades if downgrades else "pass"}
migrations/versions/0001_create_account.py
"""create account"""
revision = "0001"
down_revision = None
from alembic import op
import sqlalchemy as sa

def upgrade():
    op.create_table("account", sa.Column("id", sa.Integer, primary_key=True), sa.Column("email", sa.String(255), nullable=False, unique=True))

def downgrade():
    op.drop_table("account")
tests/test_migrations.py
from alembic import command
from alembic.config import Config
from alembic.autogenerate import compare_metadata
from alembic.migration import MigrationContext
from sqlalchemy import create_engine, inspect
from models import Base

def cfg(url):
    c = Config()
    c.set_main_option("script_location", "migrations")
    c.set_main_option("sqlalchemy.url", url)
    return c

def test_upgrade_downgrade(tmp_path):
    url = f"sqlite:///{tmp_path}/db.sqlite"
    command.upgrade(cfg(url), "head")
    assert "account" in inspect(create_engine(url)).get_table_names()
    command.downgrade(cfg(url), "base")
    assert "account" not in inspect(create_engine(url)).get_table_names()

def test_models_match_migrations(tmp_path):
    url = f"sqlite:///{tmp_path}/db.sqlite"
    command.upgrade(cfg(url), "head")
    with create_engine(url).connect() as conn:
        diff = compare_metadata(MigrationContext.configure(conn), Base.metadata)
    assert diff == []
pytest.ini
[pytest]
pythonpath = .

Connect to ProvenPin

Add the free MCP server so your agent can pull this recipe and others like it. Same setup as the docs.

terminal
claude mcp add --transport http provenpin https://mcp.provenpin.com/mcp

Any other MCP client: Streamable HTTP at https://mcp.provenpin.com/mcp. No API key, no signup.