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

オープン 初心者向け
#1,883 コメント 1 件 リアクション 0 件 担当者 0 名 GitHub で見る

まだ誰も着手していません。

  • #1886 @anish-yen による — マージされずにクローズ

評価

難易度
2/5
見積もり時間
1〜3時間
初心者へのやさしさ
82/100
issue の種類
バグ
明瞭さ
明確に書かれている
活発さ
活発
技術スタック
python, sqlalchemy
領域
backend, databases

調査の方向性

alembic/runtime/environment.py と alembic/context.pyi にある OnVersionApplyFn の定義を読み、alembic/runtime/migration.py のキーワード引数による呼び出しと比較してください。呼び出し可能な型を、ドキュメントに記載されたキーワード引数に一致するよう更新し、関連する型チェックのカバレッジを確認してください。型チェッカーがドキュメントどおりに記述されたコールバックを受け入れ、位置専用のコールバックを拒否すれば完了です。

索引モデルが issue の本文から書いたものです。

説明

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!

主要言語
Python
スター
4.5k
フォーク
391
PR マージ指標
30日以内にマージされた PR はありません

環境構築

はじめの一歩

  1. issue を最後まで読み、次にプロジェクトのコントリビューションガイドを読みます。
  2. 着手することを issue にコメントします — 二人が同じ作業をするのを防げます。
  3. リポジトリをフォークし、ブランチを切って変更します。
  4. issue 番号を参照したプルリクエストを送ります。

sqlalchemy/alembic のほかの issue

sqlalchemy/alembic の issue をすべて見る

似ている issue

Python の issue をもっと見る

新しい issue をメールで受け取る

初心者向けの GitHub issue を短くまとめたダイジェスト。