alembic icon indicating copy to clipboard operation
alembic copied to clipboard

[typing] alter_table does not accept sa.null() as server_default

Open sk- opened this issue 6 months ago • 3 comments

Describe the bug Using server_default=sa.null() in an alter_table command triggers a typing error, even though the migration executes without any problems.

Note also that sa.null() is type checked properly when used in a column definition like sa.Column("description", sa.Text, nullable=True, server_default=sa.null()) 

 Expected behavior No typing errors should be raised for valid code.

To Reproduce The following valid code produces a typing error

def upgrade():
    op.alter_column(
        "table_name",
        "column_name",
        server_default=sa.null(),
        nullable=True,
    )

Error

error: Argument "server_default" to "alter_column" has incompatible type "Null"; expected "str | bool | Identity | Computed | TextClause | None"  [arg-type]

Versions.

  • Python: Python 3.12.9 (main, Feb 12 2025, 15:09:19) [Clang 19.1.6 ] on darwin
  • Alembic: 1.16.1
  • SQLAlchemy: 2.0.40
  • Database: postgres
  • mypy: mypy 1.15.0 (compiled: yes)

Additional context This happens because there are some inconsistencies on how server_default is typed throughout alembic's and sqlalchemy's codebases.

Sqlalchemy types server_default on Column as _ServerDefaultArgument, which is defined as


_ServerDefaultArgument = Union["FetchedValue", str, TextClause, ColumnElement[Any]]

Whereas on alembic there are 2 definitions. The signature exposed on op.pyi types server_default as

Union[str, bool, Identity, Computed, TextClause, None]


The signature defined on alembic.ddl.impl types server_default as Optional[Union[_ServerDefault, Literal[False]]], where _ServerDefault is defined as

_ServerDefault = Union["TextClause", "FetchedValue", "Function[Any]", str]

sk- avatar Jun 05 '25 14:06 sk-

hi,

thanks for reporting, and indeed noting the inconsistency, if you could send a PR to fix it would very helpful! thanks!

CaselIT avatar Jun 05 '25 17:06 CaselIT

That is not only alembic but Sqlalchemy related bug too:

is_synchronized: Mapped[bool] = mapped_column(server_default=sa.false())

Error:

Expected type 'FetchedValue | str | TextClause | ColumnElement | None', got 'False_ | False_' instead 

Skorpyon avatar Jul 14 '25 11:07 Skorpyon

could you open an issue (OR pr directly) in sqlalchemy too? thanks!

CaselIT avatar Jul 14 '25 15:07 CaselIT