from __future__ import annotations
import functools
+from typing import Any
from typing import Optional
from typing import TYPE_CHECKING
from typing import Union
from sqlalchemy.ext.compiler import compiles
from sqlalchemy.schema import Column
from sqlalchemy.schema import DDLElement
+from sqlalchemy.sql.elements import ColumnElement
from sqlalchemy.sql.elements import quoted_name
+from sqlalchemy.sql.elements import TextClause
+from sqlalchemy.sql.schema import FetchedValue
from ..util.sqla_compat import _columns_for_constraint # noqa
from ..util.sqla_compat import _find_columns # noqa
from ..util.sqla_compat import _table_for_constraint # noqa
if TYPE_CHECKING:
- from typing import Any
from sqlalchemy import Computed
from sqlalchemy import Identity
from sqlalchemy.sql.compiler import Compiled
from sqlalchemy.sql.compiler import DDLCompiler
- from sqlalchemy.sql.elements import TextClause
- from sqlalchemy.sql.functions import Function
- from sqlalchemy.sql.schema import FetchedValue
from sqlalchemy.sql.type_api import TypeEngine
from .impl import DefaultImpl
-_ServerDefault = Union["TextClause", "FetchedValue", "Function[Any]", str]
+_ServerDefaultType = Union[FetchedValue, str, TextClause, ColumnElement[Any]]
class AlterTable(DDLElement):
schema: Optional[str] = None,
existing_type: Optional[TypeEngine] = None,
existing_nullable: Optional[bool] = None,
- existing_server_default: Optional[_ServerDefault] = None,
+ existing_server_default: Optional[_ServerDefaultType] = None,
existing_comment: Optional[str] = None,
) -> None:
super().__init__(name, schema=schema)
self,
name: str,
column_name: str,
- default: Optional[_ServerDefault],
+ default: Optional[_ServerDefaultType],
**kw,
) -> None:
super().__init__(name, column_name, **kw)
def format_server_default(
compiler: DDLCompiler,
- default: Optional[_ServerDefault],
+ default: Optional[_ServerDefaultType],
) -> str:
# this can be updated to use compiler.render_default_string
# for SQLAlchemy 2.0 and above; not in 1.4
from sqlalchemy.sql.selectable import TableClause
from sqlalchemy.sql.type_api import TypeEngine
- from .base import _ServerDefault
+ from .base import _ServerDefaultType
from ..autogenerate.api import AutogenContext
from ..operations.batch import ApplyBatchImpl
from ..operations.batch import BatchOperationsImpl
*,
nullable: Optional[bool] = None,
server_default: Optional[
- Union[_ServerDefault, Literal[False]]
+ Union[_ServerDefaultType, Literal[False]]
] = False,
name: Optional[str] = None,
type_: Optional[TypeEngine] = None,
comment: Optional[Union[str, Literal[False]]] = False,
existing_comment: Optional[str] = None,
existing_type: Optional[TypeEngine] = None,
- existing_server_default: Optional[_ServerDefault] = None,
+ existing_server_default: Optional[
+ Union[_ServerDefaultType, Literal[False]]
+ ] = None,
existing_nullable: Optional[bool] = None,
existing_autoincrement: Optional[bool] = None,
**kw: Any,
from sqlalchemy.sql.selectable import TableClause
from sqlalchemy.sql.type_api import TypeEngine
- from .base import _ServerDefault
+ from .base import _ServerDefaultType
from .impl import _ReflectedConstraint
*,
nullable: Optional[bool] = None,
server_default: Optional[
- Union[_ServerDefault, Literal[False]]
+ Union[_ServerDefaultType, Literal[False]]
] = False,
name: Optional[str] = None,
type_: Optional[TypeEngine] = None,
schema: Optional[str] = None,
existing_type: Optional[TypeEngine] = None,
existing_server_default: Union[
- _ServerDefault, Literal[False], None
+ _ServerDefaultType, Literal[False], None
] = None,
existing_nullable: Optional[bool] = None,
**kw: Any,
from sqlalchemy.sql.schema import Constraint
from sqlalchemy.sql.type_api import TypeEngine
- from .base import _ServerDefault
+ from .base import _ServerDefaultType
class MySQLImpl(DefaultImpl):
*,
nullable: Optional[bool] = None,
server_default: Optional[
- Union[_ServerDefault, Literal[False]]
+ Union[_ServerDefaultType, Literal[False]]
] = False,
name: Optional[str] = None,
type_: Optional[TypeEngine] = None,
schema: Optional[str] = None,
existing_type: Optional[TypeEngine] = None,
- existing_server_default: Optional[_ServerDefault] = None,
+ existing_server_default: Optional[
+ Union[_ServerDefaultType, Literal[False]]
+ ] = None,
existing_nullable: Optional[bool] = None,
autoincrement: Optional[bool] = None,
existing_autoincrement: Optional[bool] = None,
def _is_mysql_allowed_functional_default(
self,
type_: Optional[TypeEngine],
- server_default: Optional[Union[_ServerDefault, Literal[False]]],
+ server_default: Optional[Union[_ServerDefaultType, Literal[False]]],
) -> bool:
return (
type_ is not None
self,
name: str,
column_name: str,
- default: Optional[_ServerDefault],
+ default: Optional[_ServerDefaultType],
schema: Optional[str] = None,
) -> None:
super(AlterColumn, self).__init__(name, schema=schema)
newname: Optional[str] = None,
type_: Optional[TypeEngine] = None,
nullable: Optional[bool] = None,
- default: Optional[Union[_ServerDefault, Literal[False]]] = False,
+ default: Optional[Union[_ServerDefaultType, Literal[False]]] = False,
autoincrement: Optional[bool] = None,
comment: Optional[Union[str, Literal[False]]] = False,
) -> None:
def _mysql_colspec(
compiler: MySQLDDLCompiler,
nullable: Optional[bool],
- server_default: Optional[Union[_ServerDefault, Literal[False]]],
+ server_default: Optional[Union[_ServerDefaultType, Literal[False]]],
type_: TypeEngine,
autoincrement: Optional[bool],
comment: Optional[Union[str, Literal[False]]],
from sqlalchemy.sql.schema import Table
from sqlalchemy.sql.type_api import TypeEngine
- from .base import _ServerDefault
+ from .base import _ServerDefaultType
from .impl import _ReflectedConstraint
from ..autogenerate.api import AutogenContext
from ..autogenerate.render import _f_name
*,
nullable: Optional[bool] = None,
server_default: Optional[
- Union[_ServerDefault, Literal[False]]
+ Union[_ServerDefaultType, Literal[False]]
] = False,
name: Optional[str] = None,
type_: Optional[TypeEngine] = None,
schema: Optional[str] = None,
autoincrement: Optional[bool] = None,
existing_type: Optional[TypeEngine] = None,
- existing_server_default: Optional[_ServerDefault] = None,
+ existing_server_default: Optional[
+ Union[_ServerDefaultType, Literal[False]]
+ ] = None,
existing_nullable: Optional[bool] = None,
existing_autoincrement: Optional[bool] = None,
**kw: Any,
from sqlalchemy.sql.elements import TextClause
from sqlalchemy.sql.expression import TableClause
from sqlalchemy.sql.schema import Column
- from sqlalchemy.sql.schema import Computed
- from sqlalchemy.sql.schema import Identity
from sqlalchemy.sql.schema import SchemaItem
from sqlalchemy.sql.schema import Table
from sqlalchemy.sql.type_api import TypeEngine
from sqlalchemy.util import immutabledict
+ from .ddl.base import _ServerDefaultType
from .operations.base import BatchOperations
from .operations.ops import AddColumnOp
from .operations.ops import AddConstraintOp
*,
nullable: Optional[bool] = None,
comment: Union[str, Literal[False], None] = False,
- server_default: Union[
- str, bool, Identity, Computed, TextClause, None
- ] = False,
+ server_default: Union[_ServerDefaultType, None, Literal[False]] = False,
new_column_name: Optional[str] = None,
type_: Union[TypeEngine[Any], Type[TypeEngine[Any]], None] = None,
existing_type: Union[TypeEngine[Any], Type[TypeEngine[Any]], None] = None,
existing_server_default: Union[
- str, bool, Identity, Computed, TextClause, None
+ _ServerDefaultType, None, Literal[False]
] = False,
existing_nullable: Optional[bool] = None,
existing_comment: Optional[str] = None,
from . import batch
from . import schemaobj
from .. import util
+from ..ddl.base import _ServerDefaultType
from ..util import sqla_compat
from ..util.compat import formatannotation_fwdref
from ..util.compat import inspect_formatargspec
from ..util.compat import inspect_getfullargspec
from ..util.sqla_compat import _literal_bindparam
-
if TYPE_CHECKING:
from typing import Literal
from sqlalchemy.sql.expression import TableClause
from sqlalchemy.sql.expression import TextClause
from sqlalchemy.sql.schema import Column
- from sqlalchemy.sql.schema import Computed
- from sqlalchemy.sql.schema import Identity
from sqlalchemy.sql.schema import SchemaItem
from sqlalchemy.types import TypeEngine
nullable: Optional[bool] = None,
comment: Union[str, Literal[False], None] = False,
server_default: Union[
- str, bool, Identity, Computed, TextClause, None
+ _ServerDefaultType, None, Literal[False]
] = False,
new_column_name: Optional[str] = None,
type_: Union[TypeEngine[Any], Type[TypeEngine[Any]], None] = None,
TypeEngine[Any], Type[TypeEngine[Any]], None
] = None,
existing_server_default: Union[
- str, bool, Identity, Computed, TextClause, None
+ _ServerDefaultType, None, Literal[False]
] = False,
existing_nullable: Optional[bool] = None,
existing_comment: Optional[str] = None,
*,
nullable: Optional[bool] = None,
comment: Union[str, Literal[False], None] = False,
- server_default: Any = False,
+ server_default: Union[
+ _ServerDefaultType, None, Literal[False]
+ ] = False,
new_column_name: Optional[str] = None,
type_: Union[TypeEngine[Any], Type[TypeEngine[Any]], None] = None,
existing_type: Union[
TypeEngine[Any], Type[TypeEngine[Any]], None
] = None,
existing_server_default: Union[
- str, bool, Identity, Computed, None
+ _ServerDefaultType, None, Literal[False]
] = False,
existing_nullable: Optional[bool] = None,
existing_comment: Optional[str] = None,
from sqlalchemy.engine import Dialect
from sqlalchemy.sql.elements import ColumnClause
from sqlalchemy.sql.elements import quoted_name
- from sqlalchemy.sql.functions import Function
from sqlalchemy.sql.schema import Constraint
from sqlalchemy.sql.type_api import TypeEngine
+ from ..ddl.base import _ServerDefaultType
from ..ddl.impl import DefaultImpl
table_name: str,
column_name: str,
nullable: Optional[bool] = None,
- server_default: Optional[Union[Function[Any], str, bool]] = False,
+ server_default: Union[
+ _ServerDefaultType, None, Literal[False]
+ ] = False,
name: Optional[str] = None,
type_: Optional[TypeEngine] = None,
autoincrement: Optional[Union[bool, Literal["auto"]]] = None,
from sqlalchemy.sql.elements import TextClause
from sqlalchemy.sql.schema import CheckConstraint
from sqlalchemy.sql.schema import Column
- from sqlalchemy.sql.schema import Computed
from sqlalchemy.sql.schema import Constraint
from sqlalchemy.sql.schema import ForeignKeyConstraint
- from sqlalchemy.sql.schema import Identity
from sqlalchemy.sql.schema import Index
from sqlalchemy.sql.schema import MetaData
from sqlalchemy.sql.schema import PrimaryKeyConstraint
from sqlalchemy.sql.type_api import TypeEngine
from ..autogenerate.rewriter import Rewriter
+ from ..ddl.base import _ServerDefaultType
from ..runtime.migration import MigrationContext
from ..script.revision import _RevIdType
*,
schema: Optional[str] = None,
existing_type: Optional[Any] = None,
- existing_server_default: Any = False,
+ existing_server_default: Union[
+ _ServerDefaultType, None, Literal[False]
+ ] = False,
existing_nullable: Optional[bool] = None,
existing_comment: Optional[str] = None,
modify_nullable: Optional[bool] = None,
nullable: Optional[bool] = None,
comment: Optional[Union[str, Literal[False]]] = False,
server_default: Union[
- str, bool, Identity, Computed, TextClause, None
+ _ServerDefaultType, None, Literal[False]
] = False,
new_column_name: Optional[str] = None,
type_: Optional[Union[TypeEngine[Any], Type[TypeEngine[Any]]]] = None,
Union[TypeEngine[Any], Type[TypeEngine[Any]]]
] = None,
existing_server_default: Union[
- str, bool, Identity, Computed, TextClause, None
+ _ServerDefaultType, None, Literal[False]
] = False,
existing_nullable: Optional[bool] = None,
existing_comment: Optional[str] = None,
*,
nullable: Optional[bool] = None,
comment: Optional[Union[str, Literal[False]]] = False,
- server_default: Any = False,
+ server_default: Union[
+ _ServerDefaultType, None, Literal[False]
+ ] = False,
new_column_name: Optional[str] = None,
type_: Optional[Union[TypeEngine[Any], Type[TypeEngine[Any]]]] = None,
existing_type: Optional[
Union[TypeEngine[Any], Type[TypeEngine[Any]]]
] = None,
- existing_server_default: Optional[
- Union[str, bool, Identity, Computed]
+ existing_server_default: Union[
+ _ServerDefaultType, None, Literal[False]
] = False,
existing_nullable: Optional[bool] = None,
existing_comment: Optional[str] = None,
if _count_constraint(constraint):
operations.impl.drop_constraint(constraint)
+ # some weird pyright quirk here, these have Literal[False]
+ # in their types, not sure why pyright thinks they could be True
+ assert existing_server_default is not True # type: ignore[comparison-overlap] # noqa: E501
+ assert comment is not True # type: ignore[comparison-overlap]
+
operations.impl.alter_column(
table_name,
column_name,
--- /dev/null
+.. change::
+ :tags: bug, typing
+ :tickets: 1669
+
+ Fixed typing issue where the :paramref:`.AlterColumnOp.server_default` and
+ :paramref:`.AlterColumnOp.existing_server_default` parameters failed to
+ accommodate common SQLAlchemy SQL constructs such as ``null()`` and
+ ``text()``. Pull request courtesy Sebastian Kreft.
+
import sys
from tempfile import NamedTemporaryFile
import textwrap
+import types
import typing
+from sqlalchemy.util import typing as sa_typing
+
sys.path.append(str(Path(__file__).parent.parent))
if True: # avoid flake/zimports messing with the order
from alembic.autogenerate.api import AutogenContext
+ from alembic.operations.base import _ServerDefaultType
from alembic.ddl.impl import DefaultImpl
from alembic.runtime.migration import MigrationInfo
from alembic.operations.base import BatchOperations
spec.annotations.update(annotations)
except NameError as e:
print(f"{cls.__name__}.{name} NameError: {e}", file=sys.stderr)
+ raise
name_args = spec[0]
assert name_args[0:1] == ["self"] or name_args[0:1] == ["cls"]
name_args[0:1] = []
def _formatannotation(annotation, base_module=None):
- if getattr(annotation, "__module__", None) == "typing":
+ retval = None
+ if sa_typing.is_union(annotation):
+ for ta in type_aliases:
+
+ if set(ta.__args__).issubset(annotation.__args__):
+ remainder = set(annotation.__args__).difference(
+ ta.__args__
+ )
+ retval = (
+ f"Union[{type_aliases[ta]}, "
+ f"{', '.join(sorted("None" if a is types.NoneType else repr(a) for a in remainder))}]" # noqa: E501
+ )
+ break
+
+ if retval is not None:
+ pass
+ elif annotation in type_aliases:
+ retval = type_aliases[annotation]
+ elif getattr(annotation, "__module__", None) == "typing":
retval = repr(annotation).replace("typing.", "")
elif getattr(annotation, "__module__", None) == "types":
retval = repr(annotation).replace("types.", "")
elif hasattr(annotation, "__args__") and hasattr(
annotation, "__origin__"
):
+
# generic class
retval = str(annotation)
else:
"run_async",
}
+type_aliases = {_ServerDefaultType: "_ServerDefaultType"}
+
+
cases = [
StubFileInfo(
"op",