Source code for codegen_database.alembic.schema
"""Automatic schema discovery for alembic autogenerate.
Scans a :class:`~sqlalchemy.MetaData` instance for schemas referenced by
tables and views, then registers them with
``sqlalchemy-declarative-extensions`` so its built-in schema comparator
emits the appropriate ``CREATE SCHEMA`` / ``DROP SCHEMA`` ops.
"""
from typing import TYPE_CHECKING
from sqlalchemy_declarative_extensions import Schemas, Views
if TYPE_CHECKING:
from sqlalchemy import MetaData
SYSTEM_SCHEMAS = frozenset(
{"public", "pg_catalog", "information_schema", "pg_toast"}
)
[docs]
def collect_schemas(metadata: MetaData) -> set[str]:
"""Return non-system schema names referenced by tables and views."""
schemas: set[str] = set()
for table in metadata.tables.values():
if table.schema is not None:
schemas.add(table.schema)
views: Views | None = metadata.info.get("views")
if views is not None:
for view in views.views:
if view.schema is not None:
schemas.add(view.schema)
return schemas - SYSTEM_SCHEMAS
[docs]
def register_schemas(metadata: MetaData) -> None:
"""Populate ``metadata.info["schemas"]`` from tables and views.
Merges with any schemas already registered on the metadata.
Safe to call multiple times — existing entries are preserved.
"""
discovered = collect_schemas(metadata)
if not discovered:
return
existing: Schemas | None = metadata.info.get("schemas")
already_registered = (
{s.name for s in existing.schemas} if existing is not None else set()
)
new = discovered - already_registered
if not new:
return
base = (
existing if existing is not None else Schemas(ignore_unspecified=True)
)
metadata.info["schemas"] = base.are(*new)