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,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)