alembic 1.12.1__py3-none-any.whl → 1.13.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
alembic/__init__.py CHANGED
@@ -1,6 +1,4 @@
1
- import sys
2
-
3
1
  from . import context
4
2
  from . import op
5
3
 
6
- __version__ = "1.12.1"
4
+ __version__ = "1.13.0"
@@ -17,6 +17,7 @@ from . import compare
17
17
  from . import render
18
18
  from .. import util
19
19
  from ..operations import ops
20
+ from ..util import sqla_compat
20
21
 
21
22
  """Provide the 'autogenerate' feature which can produce migration operations
22
23
  automatically."""
@@ -440,7 +441,7 @@ class AutogenContext:
440
441
  def run_object_filters(
441
442
  self,
442
443
  object_: SchemaItem,
443
- name: Optional[str],
444
+ name: sqla_compat._ConstraintName,
444
445
  type_: NameFilterType,
445
446
  reflected: bool,
446
447
  compare_to: Optional[SchemaItem],
@@ -7,12 +7,12 @@ from typing import Any
7
7
  from typing import cast
8
8
  from typing import Dict
9
9
  from typing import Iterator
10
- from typing import List
11
10
  from typing import Mapping
12
11
  from typing import Optional
13
12
  from typing import Set
14
13
  from typing import Tuple
15
14
  from typing import TYPE_CHECKING
15
+ from typing import TypeVar
16
16
  from typing import Union
17
17
 
18
18
  from sqlalchemy import event
@@ -21,10 +21,14 @@ from sqlalchemy import schema as sa_schema
21
21
  from sqlalchemy import text
22
22
  from sqlalchemy import types as sqltypes
23
23
  from sqlalchemy.sql import expression
24
+ from sqlalchemy.sql.schema import ForeignKeyConstraint
25
+ from sqlalchemy.sql.schema import Index
26
+ from sqlalchemy.sql.schema import UniqueConstraint
24
27
  from sqlalchemy.util import OrderedSet
25
28
 
26
- from alembic.ddl.base import _fk_spec
27
29
  from .. import util
30
+ from ..ddl._autogen import is_index_sig
31
+ from ..ddl._autogen import is_uq_sig
28
32
  from ..operations import ops
29
33
  from ..util import sqla_compat
30
34
 
@@ -35,10 +39,7 @@ if TYPE_CHECKING:
35
39
  from sqlalchemy.sql.elements import quoted_name
36
40
  from sqlalchemy.sql.elements import TextClause
37
41
  from sqlalchemy.sql.schema import Column
38
- from sqlalchemy.sql.schema import ForeignKeyConstraint
39
- from sqlalchemy.sql.schema import Index
40
42
  from sqlalchemy.sql.schema import Table
41
- from sqlalchemy.sql.schema import UniqueConstraint
42
43
 
43
44
  from alembic.autogenerate.api import AutogenContext
44
45
  from alembic.ddl.impl import DefaultImpl
@@ -46,6 +47,8 @@ if TYPE_CHECKING:
46
47
  from alembic.operations.ops import MigrationScript
47
48
  from alembic.operations.ops import ModifyTableOps
48
49
  from alembic.operations.ops import UpgradeOps
50
+ from ..ddl._autogen import _constraint_sig
51
+
49
52
 
50
53
  log = logging.getLogger(__name__)
51
54
 
@@ -429,102 +432,7 @@ def _compare_columns(
429
432
  log.info("Detected removed column '%s.%s'", name, cname)
430
433
 
431
434
 
432
- class _constraint_sig:
433
- const: Union[UniqueConstraint, ForeignKeyConstraint, Index]
434
-
435
- def md_name_to_sql_name(self, context: AutogenContext) -> Optional[str]:
436
- return sqla_compat._get_constraint_final_name(
437
- self.const, context.dialect
438
- )
439
-
440
- def __eq__(self, other):
441
- return self.const == other.const
442
-
443
- def __ne__(self, other):
444
- return self.const != other.const
445
-
446
- def __hash__(self) -> int:
447
- return hash(self.const)
448
-
449
-
450
- class _uq_constraint_sig(_constraint_sig):
451
- is_index = False
452
- is_unique = True
453
-
454
- def __init__(self, const: UniqueConstraint, impl: DefaultImpl) -> None:
455
- self.const = const
456
- self.name = const.name
457
- self.sig = ("UNIQUE_CONSTRAINT",) + impl.create_unique_constraint_sig(
458
- const
459
- )
460
-
461
- @property
462
- def column_names(self) -> List[str]:
463
- return [col.name for col in self.const.columns]
464
-
465
-
466
- class _ix_constraint_sig(_constraint_sig):
467
- is_index = True
468
-
469
- def __init__(self, const: Index, impl: DefaultImpl) -> None:
470
- self.const = const
471
- self.name = const.name
472
- self.sig = ("INDEX",) + impl.create_index_sig(const)
473
- self.is_unique = bool(const.unique)
474
-
475
- def md_name_to_sql_name(self, context: AutogenContext) -> Optional[str]:
476
- return sqla_compat._get_constraint_final_name(
477
- self.const, context.dialect
478
- )
479
-
480
- @property
481
- def column_names(self) -> Union[List[quoted_name], List[None]]:
482
- return sqla_compat._get_index_column_names(self.const)
483
-
484
-
485
- class _fk_constraint_sig(_constraint_sig):
486
- def __init__(
487
- self, const: ForeignKeyConstraint, include_options: bool = False
488
- ) -> None:
489
- self.const = const
490
- self.name = const.name
491
-
492
- (
493
- self.source_schema,
494
- self.source_table,
495
- self.source_columns,
496
- self.target_schema,
497
- self.target_table,
498
- self.target_columns,
499
- onupdate,
500
- ondelete,
501
- deferrable,
502
- initially,
503
- ) = _fk_spec(const)
504
-
505
- self.sig: Tuple[Any, ...] = (
506
- self.source_schema,
507
- self.source_table,
508
- tuple(self.source_columns),
509
- self.target_schema,
510
- self.target_table,
511
- tuple(self.target_columns),
512
- )
513
- if include_options:
514
- self.sig += (
515
- (None if onupdate.lower() == "no action" else onupdate.lower())
516
- if onupdate
517
- else None,
518
- (None if ondelete.lower() == "no action" else ondelete.lower())
519
- if ondelete
520
- else None,
521
- # convert initially + deferrable into one three-state value
522
- "initially_deferrable"
523
- if initially and initially.lower() == "deferred"
524
- else "deferrable"
525
- if deferrable
526
- else "not deferrable",
527
- )
435
+ _C = TypeVar("_C", bound=Union[UniqueConstraint, ForeignKeyConstraint, Index])
528
436
 
529
437
 
530
438
  @comparators.dispatch_for("table")
@@ -561,32 +469,31 @@ def _compare_indexes_and_uniques(
561
469
 
562
470
  if conn_table is not None:
563
471
  # 1b. ... and from connection, if the table exists
564
- if hasattr(inspector, "get_unique_constraints"):
565
- try:
566
- conn_uniques = inspector.get_unique_constraints( # type:ignore[assignment] # noqa
567
- tname, schema=schema
472
+ try:
473
+ conn_uniques = inspector.get_unique_constraints( # type:ignore[assignment] # noqa
474
+ tname, schema=schema
475
+ )
476
+ supports_unique_constraints = True
477
+ except NotImplementedError:
478
+ pass
479
+ except TypeError:
480
+ # number of arguments is off for the base
481
+ # method in SQLAlchemy due to the cache decorator
482
+ # not being present
483
+ pass
484
+ else:
485
+ conn_uniques = [ # type:ignore[assignment]
486
+ uq
487
+ for uq in conn_uniques
488
+ if autogen_context.run_name_filters(
489
+ uq["name"],
490
+ "unique_constraint",
491
+ {"table_name": tname, "schema_name": schema},
568
492
  )
569
- supports_unique_constraints = True
570
- except NotImplementedError:
571
- pass
572
- except TypeError:
573
- # number of arguments is off for the base
574
- # method in SQLAlchemy due to the cache decorator
575
- # not being present
576
- pass
577
- else:
578
- conn_uniques = [ # type:ignore[assignment]
579
- uq
580
- for uq in conn_uniques
581
- if autogen_context.run_name_filters(
582
- uq["name"],
583
- "unique_constraint",
584
- {"table_name": tname, "schema_name": schema},
585
- )
586
- ]
587
- for uq in conn_uniques:
588
- if uq.get("duplicates_index"):
589
- unique_constraints_duplicate_unique_indexes = True
493
+ ]
494
+ for uq in conn_uniques:
495
+ if uq.get("duplicates_index"):
496
+ unique_constraints_duplicate_unique_indexes = True
590
497
  try:
591
498
  conn_indexes = inspector.get_indexes( # type:ignore[assignment]
592
499
  tname, schema=schema
@@ -639,7 +546,7 @@ def _compare_indexes_and_uniques(
639
546
  # 3. give the dialect a chance to omit indexes and constraints that
640
547
  # we know are either added implicitly by the DB or that the DB
641
548
  # can't accurately report on
642
- autogen_context.migration_context.impl.correct_for_autogen_constraints(
549
+ impl.correct_for_autogen_constraints(
643
550
  conn_uniques, # type: ignore[arg-type]
644
551
  conn_indexes, # type: ignore[arg-type]
645
552
  metadata_unique_constraints,
@@ -651,18 +558,21 @@ def _compare_indexes_and_uniques(
651
558
  # Index and UniqueConstraint so we can easily work with them
652
559
  # interchangeably
653
560
  metadata_unique_constraints_sig = {
654
- _uq_constraint_sig(uq, impl) for uq in metadata_unique_constraints
561
+ impl._create_metadata_constraint_sig(uq)
562
+ for uq in metadata_unique_constraints
655
563
  }
656
564
 
657
565
  metadata_indexes_sig = {
658
- _ix_constraint_sig(ix, impl) for ix in metadata_indexes
566
+ impl._create_metadata_constraint_sig(ix) for ix in metadata_indexes
659
567
  }
660
568
 
661
569
  conn_unique_constraints = {
662
- _uq_constraint_sig(uq, impl) for uq in conn_uniques
570
+ impl._create_reflected_constraint_sig(uq) for uq in conn_uniques
663
571
  }
664
572
 
665
- conn_indexes_sig = {_ix_constraint_sig(ix, impl) for ix in conn_indexes}
573
+ conn_indexes_sig = {
574
+ impl._create_reflected_constraint_sig(ix) for ix in conn_indexes
575
+ }
666
576
 
667
577
  # 5. index things by name, for those objects that have names
668
578
  metadata_names = {
@@ -670,12 +580,11 @@ def _compare_indexes_and_uniques(
670
580
  for c in metadata_unique_constraints_sig.union(
671
581
  metadata_indexes_sig # type:ignore[arg-type]
672
582
  )
673
- if isinstance(c, _ix_constraint_sig)
674
- or sqla_compat._constraint_is_named(c.const, autogen_context.dialect)
583
+ if c.is_named
675
584
  }
676
585
 
677
- conn_uniques_by_name: Dict[sqla_compat._ConstraintName, _uq_constraint_sig]
678
- conn_indexes_by_name: Dict[sqla_compat._ConstraintName, _ix_constraint_sig]
586
+ conn_uniques_by_name: Dict[sqla_compat._ConstraintName, _constraint_sig]
587
+ conn_indexes_by_name: Dict[sqla_compat._ConstraintName, _constraint_sig]
679
588
 
680
589
  conn_uniques_by_name = {c.name: c for c in conn_unique_constraints}
681
590
  conn_indexes_by_name = {c.name: c for c in conn_indexes_sig}
@@ -694,13 +603,12 @@ def _compare_indexes_and_uniques(
694
603
 
695
604
  # 6. index things by "column signature", to help with unnamed unique
696
605
  # constraints.
697
- conn_uniques_by_sig = {uq.sig: uq for uq in conn_unique_constraints}
606
+ conn_uniques_by_sig = {uq.unnamed: uq for uq in conn_unique_constraints}
698
607
  metadata_uniques_by_sig = {
699
- uq.sig: uq for uq in metadata_unique_constraints_sig
608
+ uq.unnamed: uq for uq in metadata_unique_constraints_sig
700
609
  }
701
- metadata_indexes_by_sig = {ix.sig: ix for ix in metadata_indexes_sig}
702
610
  unnamed_metadata_uniques = {
703
- uq.sig: uq
611
+ uq.unnamed: uq
704
612
  for uq in metadata_unique_constraints_sig
705
613
  if not sqla_compat._constraint_is_named(
706
614
  uq.const, autogen_context.dialect
@@ -715,18 +623,18 @@ def _compare_indexes_and_uniques(
715
623
  # 4. The backend may double up indexes as unique constraints and
716
624
  # vice versa (e.g. MySQL, Postgresql)
717
625
 
718
- def obj_added(obj):
719
- if obj.is_index:
626
+ def obj_added(obj: _constraint_sig):
627
+ if is_index_sig(obj):
720
628
  if autogen_context.run_object_filters(
721
629
  obj.const, obj.name, "index", False, None
722
630
  ):
723
631
  modify_ops.ops.append(ops.CreateIndexOp.from_index(obj.const))
724
632
  log.info(
725
- "Detected added index '%s' on %s",
633
+ "Detected added index '%r' on '%s'",
726
634
  obj.name,
727
- ", ".join(["'%s'" % obj.column_names]),
635
+ obj.column_names,
728
636
  )
729
- else:
637
+ elif is_uq_sig(obj):
730
638
  if not supports_unique_constraints:
731
639
  # can't report unique indexes as added if we don't
732
640
  # detect them
@@ -741,13 +649,15 @@ def _compare_indexes_and_uniques(
741
649
  ops.AddConstraintOp.from_constraint(obj.const)
742
650
  )
743
651
  log.info(
744
- "Detected added unique constraint '%s' on %s",
652
+ "Detected added unique constraint %r on '%s'",
745
653
  obj.name,
746
- ", ".join(["'%s'" % obj.column_names]),
654
+ obj.column_names,
747
655
  )
656
+ else:
657
+ assert False
748
658
 
749
- def obj_removed(obj):
750
- if obj.is_index:
659
+ def obj_removed(obj: _constraint_sig):
660
+ if is_index_sig(obj):
751
661
  if obj.is_unique and not supports_unique_constraints:
752
662
  # many databases double up unique constraints
753
663
  # as unique indexes. without that list we can't
@@ -758,10 +668,8 @@ def _compare_indexes_and_uniques(
758
668
  obj.const, obj.name, "index", True, None
759
669
  ):
760
670
  modify_ops.ops.append(ops.DropIndexOp.from_index(obj.const))
761
- log.info(
762
- "Detected removed index '%s' on '%s'", obj.name, tname
763
- )
764
- else:
671
+ log.info("Detected removed index %r on %r", obj.name, tname)
672
+ elif is_uq_sig(obj):
765
673
  if is_create_table or is_drop_table:
766
674
  # if the whole table is being dropped, we don't need to
767
675
  # consider unique constraint separately
@@ -773,33 +681,40 @@ def _compare_indexes_and_uniques(
773
681
  ops.DropConstraintOp.from_constraint(obj.const)
774
682
  )
775
683
  log.info(
776
- "Detected removed unique constraint '%s' on '%s'",
684
+ "Detected removed unique constraint %r on %r",
777
685
  obj.name,
778
686
  tname,
779
687
  )
688
+ else:
689
+ assert False
690
+
691
+ def obj_changed(
692
+ old: _constraint_sig,
693
+ new: _constraint_sig,
694
+ msg: str,
695
+ ):
696
+ if is_index_sig(old):
697
+ assert is_index_sig(new)
780
698
 
781
- def obj_changed(old, new, msg):
782
- if old.is_index:
783
699
  if autogen_context.run_object_filters(
784
700
  new.const, new.name, "index", False, old.const
785
701
  ):
786
702
  log.info(
787
- "Detected changed index '%s' on '%s':%s",
788
- old.name,
789
- tname,
790
- ", ".join(msg),
703
+ "Detected changed index %r on %r: %s", old.name, tname, msg
791
704
  )
792
705
  modify_ops.ops.append(ops.DropIndexOp.from_index(old.const))
793
706
  modify_ops.ops.append(ops.CreateIndexOp.from_index(new.const))
794
- else:
707
+ elif is_uq_sig(old):
708
+ assert is_uq_sig(new)
709
+
795
710
  if autogen_context.run_object_filters(
796
711
  new.const, new.name, "unique_constraint", False, old.const
797
712
  ):
798
713
  log.info(
799
- "Detected changed unique constraint '%s' on '%s':%s",
714
+ "Detected changed unique constraint %r on %r: %s",
800
715
  old.name,
801
716
  tname,
802
- ", ".join(msg),
717
+ msg,
803
718
  )
804
719
  modify_ops.ops.append(
805
720
  ops.DropConstraintOp.from_constraint(old.const)
@@ -807,18 +722,24 @@ def _compare_indexes_and_uniques(
807
722
  modify_ops.ops.append(
808
723
  ops.AddConstraintOp.from_constraint(new.const)
809
724
  )
725
+ else:
726
+ assert False
810
727
 
811
728
  for removed_name in sorted(set(conn_names).difference(metadata_names)):
812
- conn_obj: Union[_ix_constraint_sig, _uq_constraint_sig] = conn_names[
813
- removed_name
814
- ]
815
- if not conn_obj.is_index and conn_obj.sig in unnamed_metadata_uniques:
729
+ conn_obj = conn_names[removed_name]
730
+ if (
731
+ is_uq_sig(conn_obj)
732
+ and conn_obj.unnamed in unnamed_metadata_uniques
733
+ ):
816
734
  continue
817
735
  elif removed_name in doubled_constraints:
818
736
  conn_uq, conn_idx = doubled_constraints[removed_name]
819
737
  if (
820
- conn_idx.sig not in metadata_indexes_by_sig
821
- and conn_uq.sig not in metadata_uniques_by_sig
738
+ all(
739
+ conn_idx.unnamed != meta_idx.unnamed
740
+ for meta_idx in metadata_indexes_sig
741
+ )
742
+ and conn_uq.unnamed not in metadata_uniques_by_sig
822
743
  ):
823
744
  obj_removed(conn_uq)
824
745
  obj_removed(conn_idx)
@@ -830,30 +751,36 @@ def _compare_indexes_and_uniques(
830
751
 
831
752
  if existing_name in doubled_constraints:
832
753
  conn_uq, conn_idx = doubled_constraints[existing_name]
833
- if metadata_obj.is_index:
754
+ if is_index_sig(metadata_obj):
834
755
  conn_obj = conn_idx
835
756
  else:
836
757
  conn_obj = conn_uq
837
758
  else:
838
759
  conn_obj = conn_names[existing_name]
839
760
 
840
- if conn_obj.is_index != metadata_obj.is_index:
761
+ if type(conn_obj) != type(metadata_obj):
841
762
  obj_removed(conn_obj)
842
763
  obj_added(metadata_obj)
843
764
  else:
844
- msg = []
845
- if conn_obj.is_unique != metadata_obj.is_unique:
846
- msg.append(
847
- " unique=%r to unique=%r"
848
- % (conn_obj.is_unique, metadata_obj.is_unique)
765
+ comparison = metadata_obj.compare_to_reflected(conn_obj)
766
+
767
+ if comparison.is_different:
768
+ # constraint are different
769
+ obj_changed(conn_obj, metadata_obj, comparison.message)
770
+ elif comparison.is_skip:
771
+ # constraint cannot be compared, skip them
772
+ thing = (
773
+ "index" if is_index_sig(conn_obj) else "unique constraint"
849
774
  )
850
- if conn_obj.sig != metadata_obj.sig:
851
- msg.append(
852
- " expression %r to %r" % (conn_obj.sig, metadata_obj.sig)
775
+ log.info(
776
+ "Cannot compare %s %r, assuming equal and skipping. %s",
777
+ thing,
778
+ conn_obj.name,
779
+ comparison.message,
853
780
  )
854
-
855
- if msg:
856
- obj_changed(conn_obj, metadata_obj, msg)
781
+ else:
782
+ # constraint are equal
783
+ assert comparison.is_equal
857
784
 
858
785
  for added_name in sorted(set(metadata_names).difference(conn_names)):
859
786
  obj = metadata_names[added_name]
@@ -893,7 +820,7 @@ def _correct_for_uq_duplicates_uix(
893
820
  }
894
821
 
895
822
  unnamed_metadata_uqs = {
896
- _uq_constraint_sig(cons, impl).sig
823
+ impl._create_metadata_constraint_sig(cons).unnamed
897
824
  for name, cons in metadata_cons_names
898
825
  if name is None
899
826
  }
@@ -917,7 +844,9 @@ def _correct_for_uq_duplicates_uix(
917
844
  for overlap in uqs_dupe_indexes:
918
845
  if overlap not in metadata_uq_names:
919
846
  if (
920
- _uq_constraint_sig(uqs_dupe_indexes[overlap], impl).sig
847
+ impl._create_reflected_constraint_sig(
848
+ uqs_dupe_indexes[overlap]
849
+ ).unnamed
921
850
  not in unnamed_metadata_uqs
922
851
  ):
923
852
  conn_unique_constraints.discard(uqs_dupe_indexes[overlap])
@@ -1243,8 +1172,8 @@ def _compare_foreign_keys(
1243
1172
  modify_table_ops: ModifyTableOps,
1244
1173
  schema: Optional[str],
1245
1174
  tname: Union[quoted_name, str],
1246
- conn_table: Optional[Table],
1247
- metadata_table: Optional[Table],
1175
+ conn_table: Table,
1176
+ metadata_table: Table,
1248
1177
  ) -> None:
1249
1178
  # if we're doing CREATE TABLE, all FKs are created
1250
1179
  # inline within the table def
@@ -1268,15 +1197,13 @@ def _compare_foreign_keys(
1268
1197
  )
1269
1198
  ]
1270
1199
 
1271
- backend_reflects_fk_options = bool(
1272
- conn_fks_list and "options" in conn_fks_list[0]
1273
- )
1274
-
1275
1200
  conn_fks = {
1276
1201
  _make_foreign_key(const, conn_table) # type: ignore[arg-type]
1277
1202
  for const in conn_fks_list
1278
1203
  }
1279
1204
 
1205
+ impl = autogen_context.migration_context.impl
1206
+
1280
1207
  # give the dialect a chance to correct the FKs to match more
1281
1208
  # closely
1282
1209
  autogen_context.migration_context.impl.correct_for_autogen_foreignkeys(
@@ -1284,17 +1211,24 @@ def _compare_foreign_keys(
1284
1211
  )
1285
1212
 
1286
1213
  metadata_fks_sig = {
1287
- _fk_constraint_sig(fk, include_options=backend_reflects_fk_options)
1288
- for fk in metadata_fks
1214
+ impl._create_metadata_constraint_sig(fk) for fk in metadata_fks
1289
1215
  }
1290
1216
 
1291
1217
  conn_fks_sig = {
1292
- _fk_constraint_sig(fk, include_options=backend_reflects_fk_options)
1293
- for fk in conn_fks
1218
+ impl._create_reflected_constraint_sig(fk) for fk in conn_fks
1294
1219
  }
1295
1220
 
1296
- conn_fks_by_sig = {c.sig: c for c in conn_fks_sig}
1297
- metadata_fks_by_sig = {c.sig: c for c in metadata_fks_sig}
1221
+ # check if reflected FKs include options, indicating the backend
1222
+ # can reflect FK options
1223
+ if conn_fks_list and "options" in conn_fks_list[0]:
1224
+ conn_fks_by_sig = {c.unnamed: c for c in conn_fks_sig}
1225
+ metadata_fks_by_sig = {c.unnamed: c for c in metadata_fks_sig}
1226
+ else:
1227
+ # otherwise compare by sig without options added
1228
+ conn_fks_by_sig = {c.unnamed_no_options: c for c in conn_fks_sig}
1229
+ metadata_fks_by_sig = {
1230
+ c.unnamed_no_options: c for c in metadata_fks_sig
1231
+ }
1298
1232
 
1299
1233
  metadata_fks_by_name = {
1300
1234
  c.name: c for c in metadata_fks_sig if c.name is not None
@@ -164,14 +164,22 @@ def _render_modify_table(
164
164
  def _render_create_table_comment(
165
165
  autogen_context: AutogenContext, op: ops.CreateTableCommentOp
166
166
  ) -> str:
167
- templ = (
168
- "{prefix}create_table_comment(\n"
169
- "{indent}'{tname}',\n"
170
- "{indent}{comment},\n"
171
- "{indent}existing_comment={existing},\n"
172
- "{indent}schema={schema}\n"
173
- ")"
174
- )
167
+ if autogen_context._has_batch:
168
+ templ = (
169
+ "{prefix}create_table_comment(\n"
170
+ "{indent}{comment},\n"
171
+ "{indent}existing_comment={existing}\n"
172
+ ")"
173
+ )
174
+ else:
175
+ templ = (
176
+ "{prefix}create_table_comment(\n"
177
+ "{indent}'{tname}',\n"
178
+ "{indent}{comment},\n"
179
+ "{indent}existing_comment={existing},\n"
180
+ "{indent}schema={schema}\n"
181
+ ")"
182
+ )
175
183
  return templ.format(
176
184
  prefix=_alembic_autogenerate_prefix(autogen_context),
177
185
  tname=op.table_name,
@@ -188,13 +196,20 @@ def _render_create_table_comment(
188
196
  def _render_drop_table_comment(
189
197
  autogen_context: AutogenContext, op: ops.DropTableCommentOp
190
198
  ) -> str:
191
- templ = (
192
- "{prefix}drop_table_comment(\n"
193
- "{indent}'{tname}',\n"
194
- "{indent}existing_comment={existing},\n"
195
- "{indent}schema={schema}\n"
196
- ")"
197
- )
199
+ if autogen_context._has_batch:
200
+ templ = (
201
+ "{prefix}drop_table_comment(\n"
202
+ "{indent}existing_comment={existing}\n"
203
+ ")"
204
+ )
205
+ else:
206
+ templ = (
207
+ "{prefix}drop_table_comment(\n"
208
+ "{indent}'{tname}',\n"
209
+ "{indent}existing_comment={existing},\n"
210
+ "{indent}schema={schema}\n"
211
+ ")"
212
+ )
198
213
  return templ.format(
199
214
  prefix=_alembic_autogenerate_prefix(autogen_context),
200
215
  tname=op.table_name,
alembic/command.py CHANGED
@@ -290,7 +290,10 @@ def check(config: "Config") -> None:
290
290
  # the revision_context now has MigrationScript structure(s) present.
291
291
 
292
292
  migration_script = revision_context.generated_revisions[-1]
293
- diffs = migration_script.upgrade_ops.as_diffs()
293
+ diffs = []
294
+ for upgrade_ops in migration_script.upgrade_ops_list:
295
+ diffs.extend(upgrade_ops.as_diffs())
296
+
294
297
  if diffs:
295
298
  raise util.AutogenerateDiffsDetected(
296
299
  f"New upgrade operations detected: {diffs}"
alembic/context.pyi CHANGED
@@ -14,6 +14,7 @@ from typing import Mapping
14
14
  from typing import MutableMapping
15
15
  from typing import Optional
16
16
  from typing import overload
17
+ from typing import Sequence
17
18
  from typing import TextIO
18
19
  from typing import Tuple
19
20
  from typing import TYPE_CHECKING
@@ -97,7 +98,7 @@ def configure(
97
98
  tag: Optional[str] = None,
98
99
  template_args: Optional[Dict[str, Any]] = None,
99
100
  render_as_batch: bool = False,
100
- target_metadata: Optional[MetaData] = None,
101
+ target_metadata: Union[MetaData, Sequence[MetaData], None] = None,
101
102
  include_name: Optional[
102
103
  Callable[
103
104
  [