on_version_apply is typed as a positional callable, but alembic calls it with keyword arguments

Abierto Apto para principiantes
#1,883 1 comentario 0 reacciones 0 asignados Ver en GitHub

Nadie ha tomado este issue todavía.

  • #1886 de @anish-yen — cerrado sin fusionar

Evaluación

Dificultad
2/5
Tiempo estimado
1-3 horas
Aptitud para principiantes
82/100
Tipo de issue
Error
Claridad
Bien especificado
Estado de actividad
Activo
Stack tecnológico
python, sqlalchemy

Línea de trabajo

Lee las definiciones de OnVersionApplyFn en alembic/runtime/environment.py y alembic/context.pyi y compáralas con la llamada con argumentos nombrados en alembic/runtime/migration.py. Actualiza el tipo invocable para que coincida con los argumentos nombrados documentados y revisa la cobertura de tipado relacionada. Se considerará terminado cuando los verificadores de tipos acepten un callback escrito como se documenta y rechacen uno que solo admita argumentos posicionales.

Escrito por el modelo de indexación a partir del texto del issue.

Descripción

Describe the bug

EnvironmentContext.configure(on_version_apply=...) documents that "Each callable accepts no positional arguments and the following keyword arguments: ctx, step, heads, run_args", and MigrationContext.run_migrations() calls each callable that way (alembic/runtime/migration.py):

callback(
    ctx=self,
    step=step.info,
    heads=set(head_maintainer.heads),
    run_args=kw,
)

But the parameter is typed as a callable of four positional arguments, in alembic/runtime/environment.py and in alembic/context.pyi:

OnVersionApplyFn = Callable[
    [MigrationContext, "MigrationInfo", Collection[Any], Mapping[str, Any]],
    None,
]

So a type checker rejects a callback written as documented, and accepts one that fails at runtime.

Expected behavior

The type of on_version_apply describes the keyword arguments, for example:

class OnVersionApplyFn(Protocol):
    def __call__(
        self,
        *,
        ctx: MigrationContext,
        step: MigrationInfo,
        heads: Collection[Any],
        run_args: Mapping[str, Any],
    ) -> None: ...

To Reproduce

from typing import Any

from alembic import context
from alembic.runtime.migration import MigrationContext, MigrationInfo


def as_documented(
    *,
    ctx: MigrationContext,
    step: MigrationInfo,
    heads: set[str],
    run_args: dict[str, Any],
) -> None:
    """Takes the arguments by keyword, as documented."""


context.configure(on_version_apply=as_documented)  # rejected by type checkers


def as_typed(
    context_: MigrationContext, step: MigrationInfo, heads: Any, args: Any
) -> None:
    """Takes them by place, as typed."""


context.configure(on_version_apply=as_typed)  # accepted; run_migrations() then fails

Error

pyright 1.1.407:
error: Argument of type "(*, ctx: MigrationContext, step: MigrationInfo, heads: set[str], run_args: dict[str, Any]) -> None" cannot be assigned to parameter "on_version_apply" of type "((MigrationContext, MigrationInfo, Collection[Any], Mapping[str, Any]) -> None) | None" in function "configure" (reportArgumentType)

pyrefly 1.3.2:
ERROR Argument `(*, ctx: MigrationContext, step: MigrationInfo, heads: set[str], run_args: dict[str, Any]) -> None` is not assignable to parameter `on_version_apply` with type `((MigrationContext, MigrationInfo, Collection[Any], Mapping[str, Any]) -> None) | None` in function `alembic.context.configure` [bad-argument-type]

at runtime, with as_typed:
TypeError: as_typed() got an unexpected keyword argument 'ctx'

Versions.

  • OS: Linux, macOS
  • Python: 3.14
  • Alembic: 1.20.0
  • SQLAlchemy: 2.0.54
  • Database: PostgreSQL 17
  • DBAPI: asyncpg

Additional context

Our workaround takes the arguments both ways: def on_apply(ctx, step, heads, run_args) -> None, the parameters named as alembic passes them.

Have a nice day!

Lenguaje dominante
Python
Estrellas
4.5k
Forks
391
Métricas de merge de PR
Sin PR fusionados en 30 d

Preparar el entorno

Primeros pasos

  1. Lee el issue completo y luego la guía de contribución del proyecto.
  2. Comenta en el issue que vas a ocuparte — evita que dos personas hagan lo mismo.
  3. Haz un fork del repositorio y trabaja en una rama.
  4. Abre un pull request que haga referencia al número del issue.

Más de sqlalchemy/alembic

Todos los issues de sqlalchemy/alembic

Issues similares

Más issues de Python

Recibe los nuevos issues en tu correo

Un resumen breve de issues de GitHub para principiantes.