API refactor
All checks were successful
continuous-integration/drone/push Build is passing

This commit is contained in:
2025-10-07 16:25:52 +09:00
parent 76d0d86211
commit 91c7e04474
1171 changed files with 81940 additions and 44117 deletions

View File

@@ -1,3 +1,5 @@
# mypy: allow-untyped-calls
from __future__ import annotations
from contextlib import contextmanager
@@ -10,7 +12,9 @@ from typing import Dict
from typing import Iterator
from typing import List # noqa
from typing import Mapping
from typing import NoReturn
from typing import Optional
from typing import overload
from typing import Sequence # noqa
from typing import Tuple
from typing import Type # noqa
@@ -39,7 +43,6 @@ if TYPE_CHECKING:
from sqlalchemy.sql.expression import ColumnElement
from sqlalchemy.sql.expression import TableClause
from sqlalchemy.sql.expression import TextClause
from sqlalchemy.sql.functions import Function
from sqlalchemy.sql.schema import Column
from sqlalchemy.sql.schema import Computed
from sqlalchemy.sql.schema import Identity
@@ -47,12 +50,28 @@ if TYPE_CHECKING:
from sqlalchemy.types import TypeEngine
from .batch import BatchOperationsImpl
from .ops import AddColumnOp
from .ops import AddConstraintOp
from .ops import AlterColumnOp
from .ops import AlterTableOp
from .ops import BulkInsertOp
from .ops import CreateIndexOp
from .ops import CreateTableCommentOp
from .ops import CreateTableOp
from .ops import DropColumnOp
from .ops import DropConstraintOp
from .ops import DropIndexOp
from .ops import DropTableCommentOp
from .ops import DropTableOp
from .ops import ExecuteSQLOp
from .ops import MigrateOperation
from ..ddl import DefaultImpl
from ..runtime.migration import MigrationContext
__all__ = ("Operations", "BatchOperations")
_T = TypeVar("_T")
_C = TypeVar("_C", bound=Callable[..., Any])
class AbstractOperations(util.ModuleClsProxy):
"""Base class for Operations and BatchOperations.
@@ -86,7 +105,7 @@ class AbstractOperations(util.ModuleClsProxy):
@classmethod
def register_operation(
cls, name: str, sourcename: Optional[str] = None
) -> Callable[[_T], _T]:
) -> Callable[[Type[_T]], Type[_T]]:
"""Register a new operation for this class.
This method is normally used to add new operations
@@ -103,7 +122,7 @@ class AbstractOperations(util.ModuleClsProxy):
"""
def register(op_cls):
def register(op_cls: Type[_T]) -> Type[_T]:
if sourcename is None:
fn = getattr(op_cls, name)
source_name = fn.__name__
@@ -122,8 +141,11 @@ class AbstractOperations(util.ModuleClsProxy):
*spec, formatannotation=formatannotation_fwdref
)
num_defaults = len(spec[3]) if spec[3] else 0
defaulted_vals: Tuple[Any, ...]
if num_defaults:
defaulted_vals = name_args[0 - num_defaults :]
defaulted_vals = tuple(name_args[0 - num_defaults :])
else:
defaulted_vals = ()
@@ -164,7 +186,7 @@ class AbstractOperations(util.ModuleClsProxy):
globals_ = dict(globals())
globals_.update({"op_cls": op_cls})
lcl = {}
lcl: Dict[str, Any] = {}
exec(func_text, globals_, lcl)
setattr(cls, name, lcl[name])
@@ -180,7 +202,7 @@ class AbstractOperations(util.ModuleClsProxy):
return register
@classmethod
def implementation_for(cls, op_cls: Any) -> Callable[..., Any]:
def implementation_for(cls, op_cls: Any) -> Callable[[_C], _C]:
"""Register an implementation for a given :class:`.MigrateOperation`.
This is part of the operation extensibility API.
@@ -191,7 +213,7 @@ class AbstractOperations(util.ModuleClsProxy):
"""
def decorate(fn):
def decorate(fn: _C) -> _C:
cls._to_impl.dispatch_for(op_cls)(fn)
return fn
@@ -213,7 +235,7 @@ class AbstractOperations(util.ModuleClsProxy):
table_name: str,
schema: Optional[str] = None,
recreate: Literal["auto", "always", "never"] = "auto",
partial_reordering: Optional[tuple] = None,
partial_reordering: Optional[Tuple[Any, ...]] = None,
copy_from: Optional[Table] = None,
table_args: Tuple[Any, ...] = (),
table_kwargs: Mapping[str, Any] = util.immutabledict(),
@@ -382,6 +404,32 @@ class AbstractOperations(util.ModuleClsProxy):
return self.migration_context
@overload
def invoke(self, operation: CreateTableOp) -> Table: ...
@overload
def invoke(
self,
operation: Union[
AddConstraintOp,
DropConstraintOp,
CreateIndexOp,
DropIndexOp,
AddColumnOp,
AlterColumnOp,
AlterTableOp,
CreateTableCommentOp,
DropTableCommentOp,
DropColumnOp,
BulkInsertOp,
DropTableOp,
ExecuteSQLOp,
],
) -> None: ...
@overload
def invoke(self, operation: MigrateOperation) -> Any: ...
def invoke(self, operation: MigrateOperation) -> Any:
"""Given a :class:`.MigrateOperation`, invoke it in terms of
this :class:`.Operations` instance.
@@ -416,7 +464,7 @@ class AbstractOperations(util.ModuleClsProxy):
names will be converted along conventions. If the ``target_metadata``
contains the naming convention
``{"ck": "ck_bool_%(table_name)s_%(constraint_name)s"}``, then the
output of the following:
output of the following::
op.add_column("t", "x", Boolean(name="x"))
@@ -570,6 +618,7 @@ class Operations(AbstractOperations):
column: Column[Any],
*,
schema: Optional[str] = None,
if_not_exists: Optional[bool] = None,
) -> None:
"""Issue an "add column" instruction using the current
migration context.
@@ -646,6 +695,10 @@ class Operations(AbstractOperations):
quoting of the schema outside of the default behavior, use
the SQLAlchemy construct
:class:`~sqlalchemy.sql.elements.quoted_name`.
:param if_not_exists: If True, adds IF NOT EXISTS operator
when creating the new column for compatible dialects
.. versionadded:: 1.16.0
""" # noqa: E501
...
@@ -657,12 +710,16 @@ class Operations(AbstractOperations):
*,
nullable: Optional[bool] = None,
comment: Union[str, Literal[False], None] = False,
server_default: Any = False,
server_default: Union[
str, bool, Identity, Computed, TextClause, None
] = False,
new_column_name: Optional[str] = None,
type_: Union[TypeEngine, Type[TypeEngine], None] = None,
existing_type: Union[TypeEngine, Type[TypeEngine], None] = 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
str, bool, Identity, Computed, TextClause, None
] = False,
existing_nullable: Optional[bool] = None,
existing_comment: Optional[str] = None,
@@ -756,7 +813,7 @@ class Operations(AbstractOperations):
def bulk_insert(
self,
table: Union[Table, TableClause],
rows: List[dict],
rows: List[Dict[str, Any]],
*,
multiinsert: bool = True,
) -> None:
@@ -1023,7 +1080,7 @@ class Operations(AbstractOperations):
self,
index_name: Optional[str],
table_name: str,
columns: Sequence[Union[str, TextClause, Function[Any]]],
columns: Sequence[Union[str, TextClause, ColumnElement[Any]]],
*,
schema: Optional[str] = None,
unique: bool = False,
@@ -1124,7 +1181,11 @@ class Operations(AbstractOperations):
...
def create_table(
self, table_name: str, *columns: SchemaItem, **kw: Any
self,
table_name: str,
*columns: SchemaItem,
if_not_exists: Optional[bool] = None,
**kw: Any,
) -> Table:
r"""Issue a "create table" instruction using the current migration
context.
@@ -1196,6 +1257,10 @@ class Operations(AbstractOperations):
quoting of the schema outside of the default behavior, use
the SQLAlchemy construct
:class:`~sqlalchemy.sql.elements.quoted_name`.
:param if_not_exists: If True, adds IF NOT EXISTS operator when
creating the new table.
.. versionadded:: 1.13.3
:param \**kw: Other keyword arguments are passed to the underlying
:class:`sqlalchemy.schema.Table` object created for the command.
@@ -1301,6 +1366,11 @@ class Operations(AbstractOperations):
quoting of the schema outside of the default behavior, use
the SQLAlchemy construct
:class:`~sqlalchemy.sql.elements.quoted_name`.
:param if_exists: If True, adds IF EXISTS operator when
dropping the new column for compatible dialects
.. versionadded:: 1.16.0
:param mssql_drop_check: Optional boolean. When ``True``, on
Microsoft SQL Server only, first
drop the CHECK constraint on the column using a
@@ -1322,7 +1392,6 @@ class Operations(AbstractOperations):
then exec's a separate DROP CONSTRAINT for that default. Only
works if the column has exactly one FK constraint which refers to
it, at the moment.
""" # noqa: E501
...
@@ -1333,6 +1402,7 @@ class Operations(AbstractOperations):
type_: Optional[str] = None,
*,
schema: Optional[str] = None,
if_exists: Optional[bool] = None,
) -> None:
r"""Drop a constraint of the given name, typically via DROP CONSTRAINT.
@@ -1344,6 +1414,10 @@ class Operations(AbstractOperations):
quoting of the schema outside of the default behavior, use
the SQLAlchemy construct
:class:`~sqlalchemy.sql.elements.quoted_name`.
:param if_exists: If True, adds IF EXISTS operator when
dropping the constraint
.. versionadded:: 1.16.0
""" # noqa: E501
...
@@ -1387,7 +1461,12 @@ class Operations(AbstractOperations):
...
def drop_table(
self, table_name: str, *, schema: Optional[str] = None, **kw: Any
self,
table_name: str,
*,
schema: Optional[str] = None,
if_exists: Optional[bool] = None,
**kw: Any,
) -> None:
r"""Issue a "drop table" instruction using the current
migration context.
@@ -1402,6 +1481,10 @@ class Operations(AbstractOperations):
quoting of the schema outside of the default behavior, use
the SQLAlchemy construct
:class:`~sqlalchemy.sql.elements.quoted_name`.
:param if_exists: If True, adds IF EXISTS operator when
dropping the table.
.. versionadded:: 1.13.3
:param \**kw: Other keyword arguments are passed to the underlying
:class:`sqlalchemy.schema.Table` object created for the command.
@@ -1560,7 +1643,7 @@ class BatchOperations(AbstractOperations):
impl: BatchOperationsImpl
def _noop(self, operation):
def _noop(self, operation: Any) -> NoReturn:
raise NotImplementedError(
"The %s method does not apply to a batch table alter operation."
% operation
@@ -1577,6 +1660,7 @@ class BatchOperations(AbstractOperations):
*,
insert_before: Optional[str] = None,
insert_after: Optional[str] = None,
if_not_exists: Optional[bool] = None,
) -> None:
"""Issue an "add column" instruction using the current
batch migration context.
@@ -1596,8 +1680,10 @@ class BatchOperations(AbstractOperations):
comment: Union[str, Literal[False], None] = False,
server_default: Any = False,
new_column_name: Optional[str] = None,
type_: Union[TypeEngine, Type[TypeEngine], None] = None,
existing_type: Union[TypeEngine, Type[TypeEngine], None] = 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
] = False,
@@ -1652,7 +1738,7 @@ class BatchOperations(AbstractOperations):
def create_exclude_constraint(
self, constraint_name: str, *elements: Any, **kw: Any
):
) -> Optional[Table]:
"""Issue a "create exclude constraint" instruction using the
current batch migration context.
@@ -1668,7 +1754,7 @@ class BatchOperations(AbstractOperations):
def create_foreign_key(
self,
constraint_name: str,
constraint_name: Optional[str],
referent_table: str,
local_cols: List[str],
remote_cols: List[str],
@@ -1718,7 +1804,7 @@ class BatchOperations(AbstractOperations):
...
def create_primary_key(
self, constraint_name: str, columns: List[str]
self, constraint_name: Optional[str], columns: List[str]
) -> None:
"""Issue a "create primary key" instruction using the
current batch migration context.

View File

@@ -1,3 +1,6 @@
# mypy: allow-untyped-defs, allow-incomplete-defs, allow-untyped-calls
# mypy: no-warn-return-any, allow-any-generics
from __future__ import annotations
from typing import Any
@@ -15,9 +18,10 @@ from sqlalchemy import Index
from sqlalchemy import MetaData
from sqlalchemy import PrimaryKeyConstraint
from sqlalchemy import schema as sql_schema
from sqlalchemy import select
from sqlalchemy import Table
from sqlalchemy import types as sqltypes
from sqlalchemy.events import SchemaEventTarget
from sqlalchemy.sql.schema import SchemaEventTarget
from sqlalchemy.util import OrderedDict
from sqlalchemy.util import topological
@@ -28,11 +32,9 @@ from ..util.sqla_compat import _copy_expression
from ..util.sqla_compat import _ensure_scope_for_ddl
from ..util.sqla_compat import _fk_is_self_referential
from ..util.sqla_compat import _idx_table_bound_expressions
from ..util.sqla_compat import _insert_inline
from ..util.sqla_compat import _is_type_bound
from ..util.sqla_compat import _remove_column_from_collection
from ..util.sqla_compat import _resolve_for_variant
from ..util.sqla_compat import _select
from ..util.sqla_compat import constraint_name_defined
from ..util.sqla_compat import constraint_name_string
@@ -374,7 +376,7 @@ class ApplyBatchImpl:
for idx_existing in self.indexes.values():
# this is a lift-and-move from Table.to_metadata
if idx_existing._column_flag: # type: ignore
if idx_existing._column_flag:
continue
idx_copy = Index(
@@ -403,9 +405,7 @@ class ApplyBatchImpl:
def _setup_referent(
self, metadata: MetaData, constraint: ForeignKeyConstraint
) -> None:
spec = constraint.elements[
0
]._get_colspec() # type:ignore[attr-defined]
spec = constraint.elements[0]._get_colspec()
parts = spec.split(".")
tname = parts[-2]
if len(parts) == 3:
@@ -448,13 +448,15 @@ class ApplyBatchImpl:
try:
op_impl._exec(
_insert_inline(self.new_table).from_select(
self.new_table.insert()
.inline()
.from_select(
list(
k
for k, transfer in self.column_transfers.items()
if "expr" in transfer
),
_select(
select(
*[
transfer["expr"]
for transfer in self.column_transfers.values()
@@ -546,9 +548,7 @@ class ApplyBatchImpl:
else:
sql_schema.DefaultClause(
server_default # type: ignore[arg-type]
)._set_parent( # type:ignore[attr-defined]
existing
)
)._set_parent(existing)
if autoincrement is not None:
existing.autoincrement = bool(autoincrement)

View File

@@ -1,10 +1,13 @@
from __future__ import annotations
from abc import abstractmethod
import os
import pathlib
import re
from typing import Any
from typing import Callable
from typing import cast
from typing import Dict
from typing import FrozenSet
from typing import Iterator
from typing import List
@@ -15,6 +18,7 @@ from typing import Set
from typing import Tuple
from typing import Type
from typing import TYPE_CHECKING
from typing import TypeVar
from typing import Union
from sqlalchemy.types import NULLTYPE
@@ -33,7 +37,6 @@ if TYPE_CHECKING:
from sqlalchemy.sql.elements import conv
from sqlalchemy.sql.elements import quoted_name
from sqlalchemy.sql.elements import TextClause
from sqlalchemy.sql.functions import Function
from sqlalchemy.sql.schema import CheckConstraint
from sqlalchemy.sql.schema import Column
from sqlalchemy.sql.schema import Computed
@@ -53,6 +56,9 @@ if TYPE_CHECKING:
from ..runtime.migration import MigrationContext
from ..script.revision import _RevIdType
_T = TypeVar("_T", bound=Any)
_AC = TypeVar("_AC", bound="AddConstraintOp")
class MigrateOperation:
"""base class for migration command and organization objects.
@@ -70,7 +76,7 @@ class MigrateOperation:
"""
@util.memoized_property
def info(self):
def info(self) -> Dict[Any, Any]:
"""A dictionary that may be used to store arbitrary information
along with this :class:`.MigrateOperation` object.
@@ -92,12 +98,14 @@ class AddConstraintOp(MigrateOperation):
add_constraint_ops = util.Dispatcher()
@property
def constraint_type(self):
def constraint_type(self) -> str:
raise NotImplementedError()
@classmethod
def register_add_constraint(cls, type_: str) -> Callable:
def go(klass):
def register_add_constraint(
cls, type_: str
) -> Callable[[Type[_AC]], Type[_AC]]:
def go(klass: Type[_AC]) -> Type[_AC]:
cls.add_constraint_ops.dispatch_for(type_)(klass.from_constraint)
return klass
@@ -105,7 +113,7 @@ class AddConstraintOp(MigrateOperation):
@classmethod
def from_constraint(cls, constraint: Constraint) -> AddConstraintOp:
return cls.add_constraint_ops.dispatch(constraint.__visit_name__)(
return cls.add_constraint_ops.dispatch(constraint.__visit_name__)( # type: ignore[no-any-return] # noqa: E501
constraint
)
@@ -134,12 +142,14 @@ class DropConstraintOp(MigrateOperation):
type_: Optional[str] = None,
*,
schema: Optional[str] = None,
if_exists: Optional[bool] = None,
_reverse: Optional[AddConstraintOp] = None,
) -> None:
self.constraint_name = constraint_name
self.table_name = table_name
self.constraint_type = type_
self.schema = schema
self.if_exists = if_exists
self._reverse = _reverse
def reverse(self) -> AddConstraintOp:
@@ -197,6 +207,7 @@ class DropConstraintOp(MigrateOperation):
type_: Optional[str] = None,
*,
schema: Optional[str] = None,
if_exists: Optional[bool] = None,
) -> None:
r"""Drop a constraint of the given name, typically via DROP CONSTRAINT.
@@ -208,10 +219,20 @@ class DropConstraintOp(MigrateOperation):
quoting of the schema outside of the default behavior, use
the SQLAlchemy construct
:class:`~sqlalchemy.sql.elements.quoted_name`.
:param if_exists: If True, adds IF EXISTS operator when
dropping the constraint
.. versionadded:: 1.16.0
"""
op = cls(constraint_name, table_name, type_=type_, schema=schema)
op = cls(
constraint_name,
table_name,
type_=type_,
schema=schema,
if_exists=if_exists,
)
return operations.invoke(op)
@classmethod
@@ -342,7 +363,7 @@ class CreatePrimaryKeyOp(AddConstraintOp):
def batch_create_primary_key(
cls,
operations: BatchOperations,
constraint_name: str,
constraint_name: Optional[str],
columns: List[str],
) -> None:
"""Issue a "create primary key" instruction using the
@@ -398,7 +419,7 @@ class CreateUniqueConstraintOp(AddConstraintOp):
uq_constraint = cast("UniqueConstraint", constraint)
kw: dict = {}
kw: Dict[str, Any] = {}
if uq_constraint.deferrable:
kw["deferrable"] = uq_constraint.deferrable
if uq_constraint.initially:
@@ -532,7 +553,7 @@ class CreateForeignKeyOp(AddConstraintOp):
@classmethod
def from_constraint(cls, constraint: Constraint) -> CreateForeignKeyOp:
fk_constraint = cast("ForeignKeyConstraint", constraint)
kw: dict = {}
kw: Dict[str, Any] = {}
if fk_constraint.onupdate:
kw["onupdate"] = fk_constraint.onupdate
if fk_constraint.ondelete:
@@ -674,7 +695,7 @@ class CreateForeignKeyOp(AddConstraintOp):
def batch_create_foreign_key(
cls,
operations: BatchOperations,
constraint_name: str,
constraint_name: Optional[str],
referent_table: str,
local_cols: List[str],
remote_cols: List[str],
@@ -897,9 +918,9 @@ class CreateIndexOp(MigrateOperation):
def from_index(cls, index: Index) -> CreateIndexOp:
assert index.table is not None
return cls(
index.name, # type: ignore[arg-type]
index.name,
index.table.name,
sqla_compat._get_index_expressions(index),
index.expressions,
schema=index.table.schema,
unique=index.unique,
**index.kwargs,
@@ -926,7 +947,7 @@ class CreateIndexOp(MigrateOperation):
operations: Operations,
index_name: Optional[str],
table_name: str,
columns: Sequence[Union[str, TextClause, Function[Any]]],
columns: Sequence[Union[str, TextClause, ColumnElement[Any]]],
*,
schema: Optional[str] = None,
unique: bool = False,
@@ -1054,6 +1075,7 @@ class DropIndexOp(MigrateOperation):
table_name=index.table.name,
schema=index.table.schema,
_reverse=CreateIndexOp.from_index(index),
unique=index.unique,
**index.kwargs,
)
@@ -1151,6 +1173,7 @@ class CreateTableOp(MigrateOperation):
columns: Sequence[SchemaItem],
*,
schema: Optional[str] = None,
if_not_exists: Optional[bool] = None,
_namespace_metadata: Optional[MetaData] = None,
_constraints_included: bool = False,
**kw: Any,
@@ -1158,6 +1181,7 @@ class CreateTableOp(MigrateOperation):
self.table_name = table_name
self.columns = columns
self.schema = schema
self.if_not_exists = if_not_exists
self.info = kw.pop("info", {})
self.comment = kw.pop("comment", None)
self.prefixes = kw.pop("prefixes", None)
@@ -1182,7 +1206,7 @@ class CreateTableOp(MigrateOperation):
return cls(
table.name,
list(table.c) + list(table.constraints), # type:ignore[arg-type]
list(table.c) + list(table.constraints),
schema=table.schema,
_namespace_metadata=_namespace_metadata,
# given a Table() object, this Table will contain full Index()
@@ -1220,6 +1244,7 @@ class CreateTableOp(MigrateOperation):
operations: Operations,
table_name: str,
*columns: SchemaItem,
if_not_exists: Optional[bool] = None,
**kw: Any,
) -> Table:
r"""Issue a "create table" instruction using the current migration
@@ -1292,6 +1317,10 @@ class CreateTableOp(MigrateOperation):
quoting of the schema outside of the default behavior, use
the SQLAlchemy construct
:class:`~sqlalchemy.sql.elements.quoted_name`.
:param if_not_exists: If True, adds IF NOT EXISTS operator when
creating the new table.
.. versionadded:: 1.13.3
:param \**kw: Other keyword arguments are passed to the underlying
:class:`sqlalchemy.schema.Table` object created for the command.
@@ -1299,7 +1328,7 @@ class CreateTableOp(MigrateOperation):
to the parameters given.
"""
op = cls(table_name, columns, **kw)
op = cls(table_name, columns, if_not_exists=if_not_exists, **kw)
return operations.invoke(op)
@@ -1312,11 +1341,13 @@ class DropTableOp(MigrateOperation):
table_name: str,
*,
schema: Optional[str] = None,
if_exists: Optional[bool] = None,
table_kw: Optional[MutableMapping[Any, Any]] = None,
_reverse: Optional[CreateTableOp] = None,
) -> None:
self.table_name = table_name
self.schema = schema
self.if_exists = if_exists
self.table_kw = table_kw or {}
self.comment = self.table_kw.pop("comment", None)
self.info = self.table_kw.pop("info", None)
@@ -1363,9 +1394,9 @@ class DropTableOp(MigrateOperation):
info=self.info.copy() if self.info else {},
prefixes=list(self.prefixes) if self.prefixes else [],
schema=self.schema,
_constraints_included=self._reverse._constraints_included
if self._reverse
else False,
_constraints_included=(
self._reverse._constraints_included if self._reverse else False
),
**self.table_kw,
)
return t
@@ -1377,6 +1408,7 @@ class DropTableOp(MigrateOperation):
table_name: str,
*,
schema: Optional[str] = None,
if_exists: Optional[bool] = None,
**kw: Any,
) -> None:
r"""Issue a "drop table" instruction using the current
@@ -1392,11 +1424,15 @@ class DropTableOp(MigrateOperation):
quoting of the schema outside of the default behavior, use
the SQLAlchemy construct
:class:`~sqlalchemy.sql.elements.quoted_name`.
:param if_exists: If True, adds IF EXISTS operator when
dropping the table.
.. versionadded:: 1.13.3
:param \**kw: Other keyword arguments are passed to the underlying
:class:`sqlalchemy.schema.Table` object created for the command.
"""
op = cls(table_name, schema=schema, table_kw=kw)
op = cls(table_name, schema=schema, if_exists=if_exists, table_kw=kw)
operations.invoke(op)
@@ -1534,7 +1570,7 @@ class CreateTableCommentOp(AlterTableOp):
)
return operations.invoke(op)
def reverse(self):
def reverse(self) -> Union[CreateTableCommentOp, DropTableCommentOp]:
"""Reverses the COMMENT ON operation against a table."""
if self.existing_comment is None:
return DropTableCommentOp(
@@ -1550,14 +1586,16 @@ class CreateTableCommentOp(AlterTableOp):
schema=self.schema,
)
def to_table(self, migration_context=None):
def to_table(
self, migration_context: Optional[MigrationContext] = None
) -> Table:
schema_obj = schemaobj.SchemaObjects(migration_context)
return schema_obj.table(
self.table_name, schema=self.schema, comment=self.comment
)
def to_diff_tuple(self):
def to_diff_tuple(self) -> Tuple[Any, ...]:
return ("add_table_comment", self.to_table(), self.existing_comment)
@@ -1629,18 +1667,20 @@ class DropTableCommentOp(AlterTableOp):
)
return operations.invoke(op)
def reverse(self):
def reverse(self) -> CreateTableCommentOp:
"""Reverses the COMMENT ON operation against a table."""
return CreateTableCommentOp(
self.table_name, self.existing_comment, schema=self.schema
)
def to_table(self, migration_context=None):
def to_table(
self, migration_context: Optional[MigrationContext] = None
) -> Table:
schema_obj = schemaobj.SchemaObjects(migration_context)
return schema_obj.table(self.table_name, schema=self.schema)
def to_diff_tuple(self):
def to_diff_tuple(self) -> Tuple[Any, ...]:
return ("remove_table_comment", self.to_table())
@@ -1815,12 +1855,16 @@ class AlterColumnOp(AlterTableOp):
*,
nullable: Optional[bool] = None,
comment: Optional[Union[str, Literal[False]]] = False,
server_default: Any = False,
server_default: Union[
str, bool, Identity, Computed, TextClause, None
] = False,
new_column_name: Optional[str] = None,
type_: Optional[Union[TypeEngine, Type[TypeEngine]]] = None,
existing_type: Optional[Union[TypeEngine, Type[TypeEngine]]] = None,
existing_server_default: Optional[
Union[str, bool, Identity, Computed]
type_: Optional[Union[TypeEngine[Any], Type[TypeEngine[Any]]]] = None,
existing_type: Optional[
Union[TypeEngine[Any], Type[TypeEngine[Any]]]
] = None,
existing_server_default: Union[
str, bool, Identity, Computed, TextClause, None
] = False,
existing_nullable: Optional[bool] = None,
existing_comment: Optional[str] = None,
@@ -1938,8 +1982,10 @@ class AlterColumnOp(AlterTableOp):
comment: Optional[Union[str, Literal[False]]] = False,
server_default: Any = False,
new_column_name: Optional[str] = None,
type_: Optional[Union[TypeEngine, Type[TypeEngine]]] = None,
existing_type: Optional[Union[TypeEngine, Type[TypeEngine]]] = 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]
] = False,
@@ -2003,27 +2049,31 @@ class AddColumnOp(AlterTableOp):
column: Column[Any],
*,
schema: Optional[str] = None,
if_not_exists: Optional[bool] = None,
**kw: Any,
) -> None:
super().__init__(table_name, schema=schema)
self.column = column
self.if_not_exists = if_not_exists
self.kw = kw
def reverse(self) -> DropColumnOp:
return DropColumnOp.from_column_and_tablename(
op = DropColumnOp.from_column_and_tablename(
self.schema, self.table_name, self.column
)
op.if_exists = self.if_not_exists
return op
def to_diff_tuple(
self,
) -> Tuple[str, Optional[str], str, Column[Any]]:
return ("add_column", self.schema, self.table_name, self.column)
def to_column(self) -> Column:
def to_column(self) -> Column[Any]:
return self.column
@classmethod
def from_column(cls, col: Column) -> AddColumnOp:
def from_column(cls, col: Column[Any]) -> AddColumnOp:
return cls(col.table.name, col, schema=col.table.schema)
@classmethod
@@ -2043,6 +2093,7 @@ class AddColumnOp(AlterTableOp):
column: Column[Any],
*,
schema: Optional[str] = None,
if_not_exists: Optional[bool] = None,
) -> None:
"""Issue an "add column" instruction using the current
migration context.
@@ -2119,10 +2170,19 @@ class AddColumnOp(AlterTableOp):
quoting of the schema outside of the default behavior, use
the SQLAlchemy construct
:class:`~sqlalchemy.sql.elements.quoted_name`.
:param if_not_exists: If True, adds IF NOT EXISTS operator
when creating the new column for compatible dialects
.. versionadded:: 1.16.0
"""
op = cls(table_name, column, schema=schema)
op = cls(
table_name,
column,
schema=schema,
if_not_exists=if_not_exists,
)
return operations.invoke(op)
@classmethod
@@ -2133,6 +2193,7 @@ class AddColumnOp(AlterTableOp):
*,
insert_before: Optional[str] = None,
insert_after: Optional[str] = None,
if_not_exists: Optional[bool] = None,
) -> None:
"""Issue an "add column" instruction using the current
batch migration context.
@@ -2153,6 +2214,7 @@ class AddColumnOp(AlterTableOp):
operations.impl.table_name,
column,
schema=operations.impl.schema,
if_not_exists=if_not_exists,
**kw,
)
return operations.invoke(op)
@@ -2169,12 +2231,14 @@ class DropColumnOp(AlterTableOp):
column_name: str,
*,
schema: Optional[str] = None,
if_exists: Optional[bool] = None,
_reverse: Optional[AddColumnOp] = None,
**kw: Any,
) -> None:
super().__init__(table_name, schema=schema)
self.column_name = column_name
self.kw = kw
self.if_exists = if_exists
self._reverse = _reverse
def to_diff_tuple(
@@ -2194,9 +2258,11 @@ class DropColumnOp(AlterTableOp):
"original column is not present"
)
return AddColumnOp.from_column_and_tablename(
op = AddColumnOp.from_column_and_tablename(
self.schema, self.table_name, self._reverse.column
)
op.if_not_exists = self.if_exists
return op
@classmethod
def from_column_and_tablename(
@@ -2214,7 +2280,7 @@ class DropColumnOp(AlterTableOp):
def to_column(
self, migration_context: Optional[MigrationContext] = None
) -> Column:
) -> Column[Any]:
if self._reverse is not None:
return self._reverse.column
schema_obj = schemaobj.SchemaObjects(migration_context)
@@ -2243,6 +2309,11 @@ class DropColumnOp(AlterTableOp):
quoting of the schema outside of the default behavior, use
the SQLAlchemy construct
:class:`~sqlalchemy.sql.elements.quoted_name`.
:param if_exists: If True, adds IF EXISTS operator when
dropping the new column for compatible dialects
.. versionadded:: 1.16.0
:param mssql_drop_check: Optional boolean. When ``True``, on
Microsoft SQL Server only, first
drop the CHECK constraint on the column using a
@@ -2264,7 +2335,6 @@ class DropColumnOp(AlterTableOp):
then exec's a separate DROP CONSTRAINT for that default. Only
works if the column has exactly one FK constraint which refers to
it, at the moment.
"""
op = cls(table_name, column_name, schema=schema, **kw)
@@ -2298,7 +2368,7 @@ class BulkInsertOp(MigrateOperation):
def __init__(
self,
table: Union[Table, TableClause],
rows: List[dict],
rows: List[Dict[str, Any]],
*,
multiinsert: bool = True,
) -> None:
@@ -2311,7 +2381,7 @@ class BulkInsertOp(MigrateOperation):
cls,
operations: Operations,
table: Union[Table, TableClause],
rows: List[dict],
rows: List[Dict[str, Any]],
*,
multiinsert: bool = True,
) -> None:
@@ -2607,7 +2677,7 @@ class UpgradeOps(OpContainer):
self.upgrade_token = upgrade_token
def reverse_into(self, downgrade_ops: DowngradeOps) -> DowngradeOps:
downgrade_ops.ops[:] = list( # type:ignore[index]
downgrade_ops.ops[:] = list(
reversed([op.reverse() for op in self.ops])
)
return downgrade_ops
@@ -2634,7 +2704,7 @@ class DowngradeOps(OpContainer):
super().__init__(ops=ops)
self.downgrade_token = downgrade_token
def reverse(self):
def reverse(self) -> UpgradeOps:
return UpgradeOps(
ops=list(reversed([op.reverse() for op in self.ops]))
)
@@ -2665,6 +2735,8 @@ class MigrationScript(MigrateOperation):
"""
_needs_render: Optional[bool]
_upgrade_ops: List[UpgradeOps]
_downgrade_ops: List[DowngradeOps]
def __init__(
self,
@@ -2677,7 +2749,7 @@ class MigrationScript(MigrateOperation):
head: Optional[str] = None,
splice: Optional[bool] = None,
branch_label: Optional[_RevIdType] = None,
version_path: Optional[str] = None,
version_path: Union[str, os.PathLike[str], None] = None,
depends_on: Optional[_RevIdType] = None,
) -> None:
self.rev_id = rev_id
@@ -2686,13 +2758,15 @@ class MigrationScript(MigrateOperation):
self.head = head
self.splice = splice
self.branch_label = branch_label
self.version_path = version_path
self.version_path = (
pathlib.Path(version_path).as_posix() if version_path else None
)
self.depends_on = depends_on
self.upgrade_ops = upgrade_ops
self.downgrade_ops = downgrade_ops
@property
def upgrade_ops(self):
def upgrade_ops(self) -> Optional[UpgradeOps]:
"""An instance of :class:`.UpgradeOps`.
.. seealso::
@@ -2711,13 +2785,15 @@ class MigrationScript(MigrateOperation):
return self._upgrade_ops[0]
@upgrade_ops.setter
def upgrade_ops(self, upgrade_ops):
def upgrade_ops(
self, upgrade_ops: Union[UpgradeOps, List[UpgradeOps]]
) -> None:
self._upgrade_ops = util.to_list(upgrade_ops)
for elem in self._upgrade_ops:
assert isinstance(elem, UpgradeOps)
@property
def downgrade_ops(self):
def downgrade_ops(self) -> Optional[DowngradeOps]:
"""An instance of :class:`.DowngradeOps`.
.. seealso::
@@ -2736,7 +2812,9 @@ class MigrationScript(MigrateOperation):
return self._downgrade_ops[0]
@downgrade_ops.setter
def downgrade_ops(self, downgrade_ops):
def downgrade_ops(
self, downgrade_ops: Union[DowngradeOps, List[DowngradeOps]]
) -> None:
self._downgrade_ops = util.to_list(downgrade_ops)
for elem in self._downgrade_ops:
assert isinstance(elem, DowngradeOps)

View File

@@ -1,3 +1,6 @@
# mypy: allow-untyped-defs, allow-incomplete-defs, allow-untyped-calls
# mypy: no-warn-return-any, allow-any-generics
from __future__ import annotations
from typing import Any
@@ -220,10 +223,12 @@ class SchemaObjects:
t = sa_schema.Table(name, m, *cols, **kw)
constraints = [
sqla_compat._copy(elem, target_table=t)
if getattr(elem, "parent", None) is not t
and getattr(elem, "parent", None) is not None
else elem
(
sqla_compat._copy(elem, target_table=t)
if getattr(elem, "parent", None) is not t
and getattr(elem, "parent", None) is not None
else elem
)
for elem in columns
if isinstance(elem, (Constraint, Index))
]
@@ -274,10 +279,8 @@ class SchemaObjects:
ForeignKey.
"""
if isinstance(fk._colspec, str): # type:ignore[attr-defined]
table_key, cname = fk._colspec.rsplit( # type:ignore[attr-defined]
".", 1
)
if isinstance(fk._colspec, str):
table_key, cname = fk._colspec.rsplit(".", 1)
sname, tname = self._parse_table_key(table_key)
if table_key not in metadata.tables:
rel_t = sa_schema.Table(tname, metadata, schema=sname)

View File

@@ -1,3 +1,6 @@
# mypy: allow-untyped-defs, allow-incomplete-defs, allow-untyped-calls
# mypy: no-warn-return-any, allow-any-generics
from typing import TYPE_CHECKING
from sqlalchemy import schema as sa_schema
@@ -76,8 +79,11 @@ def alter_column(
@Operations.implementation_for(ops.DropTableOp)
def drop_table(operations: "Operations", operation: "ops.DropTableOp") -> None:
kw = {}
if operation.if_exists is not None:
kw["if_exists"] = operation.if_exists
operations.impl.drop_table(
operation.to_table(operations.migration_context)
operation.to_table(operations.migration_context), **kw
)
@@ -87,7 +93,11 @@ def drop_column(
) -> None:
column = operation.to_column(operations.migration_context)
operations.impl.drop_column(
operation.table_name, column, schema=operation.schema, **operation.kw
operation.table_name,
column,
schema=operation.schema,
if_exists=operation.if_exists,
**operation.kw,
)
@@ -98,9 +108,6 @@ def create_index(
idx = operation.to_index(operations.migration_context)
kw = {}
if operation.if_not_exists is not None:
if not sqla_2:
raise NotImplementedError("SQLAlchemy 2.0+ required")
kw["if_not_exists"] = operation.if_not_exists
operations.impl.create_index(idx, **kw)
@@ -109,9 +116,6 @@ def create_index(
def drop_index(operations: "Operations", operation: "ops.DropIndexOp") -> None:
kw = {}
if operation.if_exists is not None:
if not sqla_2:
raise NotImplementedError("SQLAlchemy 2.0+ required")
kw["if_exists"] = operation.if_exists
operations.impl.drop_index(
@@ -124,8 +128,11 @@ def drop_index(operations: "Operations", operation: "ops.DropIndexOp") -> None:
def create_table(
operations: "Operations", operation: "ops.CreateTableOp"
) -> "Table":
kw = {}
if operation.if_not_exists is not None:
kw["if_not_exists"] = operation.if_not_exists
table = operation.to_table(operations.migration_context)
operations.impl.create_table(table)
operations.impl.create_table(table, **kw)
return table
@@ -165,7 +172,13 @@ def add_column(operations: "Operations", operation: "ops.AddColumnOp") -> None:
column = _copy(column)
t = operations.schema_obj.table(table_name, column, schema=schema)
operations.impl.add_column(table_name, column, schema=schema, **kw)
operations.impl.add_column(
table_name,
column,
schema=schema,
if_not_exists=operation.if_not_exists,
**kw,
)
for constraint in t.constraints:
if not isinstance(constraint, sa_schema.PrimaryKeyConstraint):
@@ -195,13 +208,19 @@ def create_constraint(
def drop_constraint(
operations: "Operations", operation: "ops.DropConstraintOp"
) -> None:
kw = {}
if operation.if_exists is not None:
if not sqla_2:
raise NotImplementedError("SQLAlchemy 2.0 required")
kw["if_exists"] = operation.if_exists
operations.impl.drop_constraint(
operations.schema_obj.generic_constraint(
operation.constraint_name,
operation.table_name,
operation.constraint_type,
schema=operation.schema,
)
),
**kw,
)