alembic 1.13.1__py3-none-any.whl → 1.13.3__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 +1 -1
- alembic/autogenerate/api.py +3 -3
- alembic/autogenerate/compare.py +1 -1
- alembic/autogenerate/render.py +36 -15
- alembic/command.py +24 -15
- alembic/config.py +5 -10
- alembic/ddl/_autogen.py +15 -11
- alembic/ddl/base.py +5 -4
- alembic/ddl/impl.py +26 -21
- alembic/ddl/mysql.py +49 -31
- alembic/ddl/oracle.py +5 -3
- alembic/ddl/postgresql.py +2 -1
- alembic/op.pyi +19 -2
- alembic/operations/base.py +24 -10
- alembic/operations/ops.py +21 -7
- alembic/operations/schemaobj.py +6 -4
- alembic/operations/toimpl.py +14 -2
- alembic/runtime/environment.py +5 -7
- alembic/runtime/migration.py +9 -9
- alembic/script/base.py +17 -11
- alembic/script/revision.py +25 -18
- alembic/templates/async/alembic.ini.mako +4 -3
- alembic/templates/generic/alembic.ini.mako +3 -2
- alembic/templates/multidb/alembic.ini.mako +3 -2
- alembic/testing/assertions.py +13 -4
- alembic/testing/fixtures.py +20 -8
- alembic/testing/suite/test_autogen_computed.py +1 -0
- alembic/testing/suite/test_environment.py +3 -3
- alembic/util/langhelpers.py +3 -6
- alembic/util/messaging.py +9 -3
- alembic/util/sqla_compat.py +3 -5
- {alembic-1.13.1.dist-info → alembic-1.13.3.dist-info}/LICENSE +2 -2
- {alembic-1.13.1.dist-info → alembic-1.13.3.dist-info}/METADATA +1 -1
- {alembic-1.13.1.dist-info → alembic-1.13.3.dist-info}/RECORD +37 -37
- {alembic-1.13.1.dist-info → alembic-1.13.3.dist-info}/WHEEL +1 -1
- {alembic-1.13.1.dist-info → alembic-1.13.3.dist-info}/entry_points.txt +0 -0
- {alembic-1.13.1.dist-info → alembic-1.13.3.dist-info}/top_level.txt +0 -0
alembic/operations/base.py
CHANGED
@@ -406,8 +406,7 @@ class AbstractOperations(util.ModuleClsProxy):
|
|
406
406
|
return self.migration_context
|
407
407
|
|
408
408
|
@overload
|
409
|
-
def invoke(self, operation: CreateTableOp) -> Table:
|
410
|
-
...
|
409
|
+
def invoke(self, operation: CreateTableOp) -> Table: ...
|
411
410
|
|
412
411
|
@overload
|
413
412
|
def invoke(
|
@@ -427,12 +426,10 @@ class AbstractOperations(util.ModuleClsProxy):
|
|
427
426
|
DropTableOp,
|
428
427
|
ExecuteSQLOp,
|
429
428
|
],
|
430
|
-
) -> None:
|
431
|
-
...
|
429
|
+
) -> None: ...
|
432
430
|
|
433
431
|
@overload
|
434
|
-
def invoke(self, operation: MigrateOperation) -> Any:
|
435
|
-
...
|
432
|
+
def invoke(self, operation: MigrateOperation) -> Any: ...
|
436
433
|
|
437
434
|
def invoke(self, operation: MigrateOperation) -> Any:
|
438
435
|
"""Given a :class:`.MigrateOperation`, invoke it in terms of
|
@@ -1178,7 +1175,11 @@ class Operations(AbstractOperations):
|
|
1178
1175
|
...
|
1179
1176
|
|
1180
1177
|
def create_table(
|
1181
|
-
self,
|
1178
|
+
self,
|
1179
|
+
table_name: str,
|
1180
|
+
*columns: SchemaItem,
|
1181
|
+
if_not_exists: Optional[bool] = None,
|
1182
|
+
**kw: Any,
|
1182
1183
|
) -> Table:
|
1183
1184
|
r"""Issue a "create table" instruction using the current migration
|
1184
1185
|
context.
|
@@ -1250,6 +1251,10 @@ class Operations(AbstractOperations):
|
|
1250
1251
|
quoting of the schema outside of the default behavior, use
|
1251
1252
|
the SQLAlchemy construct
|
1252
1253
|
:class:`~sqlalchemy.sql.elements.quoted_name`.
|
1254
|
+
:param if_not_exists: If True, adds IF NOT EXISTS operator when
|
1255
|
+
creating the new table.
|
1256
|
+
|
1257
|
+
.. versionadded:: 1.13.3
|
1253
1258
|
:param \**kw: Other keyword arguments are passed to the underlying
|
1254
1259
|
:class:`sqlalchemy.schema.Table` object created for the command.
|
1255
1260
|
|
@@ -1441,7 +1446,12 @@ class Operations(AbstractOperations):
|
|
1441
1446
|
...
|
1442
1447
|
|
1443
1448
|
def drop_table(
|
1444
|
-
self,
|
1449
|
+
self,
|
1450
|
+
table_name: str,
|
1451
|
+
*,
|
1452
|
+
schema: Optional[str] = None,
|
1453
|
+
if_exists: Optional[bool] = None,
|
1454
|
+
**kw: Any,
|
1445
1455
|
) -> None:
|
1446
1456
|
r"""Issue a "drop table" instruction using the current
|
1447
1457
|
migration context.
|
@@ -1456,6 +1466,10 @@ class Operations(AbstractOperations):
|
|
1456
1466
|
quoting of the schema outside of the default behavior, use
|
1457
1467
|
the SQLAlchemy construct
|
1458
1468
|
:class:`~sqlalchemy.sql.elements.quoted_name`.
|
1469
|
+
:param if_exists: If True, adds IF EXISTS operator when
|
1470
|
+
dropping the table.
|
1471
|
+
|
1472
|
+
.. versionadded:: 1.13.3
|
1459
1473
|
:param \**kw: Other keyword arguments are passed to the underlying
|
1460
1474
|
:class:`sqlalchemy.schema.Table` object created for the command.
|
1461
1475
|
|
@@ -1724,7 +1738,7 @@ class BatchOperations(AbstractOperations):
|
|
1724
1738
|
|
1725
1739
|
def create_foreign_key(
|
1726
1740
|
self,
|
1727
|
-
constraint_name: str,
|
1741
|
+
constraint_name: Optional[str],
|
1728
1742
|
referent_table: str,
|
1729
1743
|
local_cols: List[str],
|
1730
1744
|
remote_cols: List[str],
|
@@ -1774,7 +1788,7 @@ class BatchOperations(AbstractOperations):
|
|
1774
1788
|
...
|
1775
1789
|
|
1776
1790
|
def create_primary_key(
|
1777
|
-
self, constraint_name: str, columns: List[str]
|
1791
|
+
self, constraint_name: Optional[str], columns: List[str]
|
1778
1792
|
) -> None:
|
1779
1793
|
"""Issue a "create primary key" instruction using the
|
1780
1794
|
current batch migration context.
|
alembic/operations/ops.py
CHANGED
@@ -349,7 +349,7 @@ class CreatePrimaryKeyOp(AddConstraintOp):
|
|
349
349
|
def batch_create_primary_key(
|
350
350
|
cls,
|
351
351
|
operations: BatchOperations,
|
352
|
-
constraint_name: str,
|
352
|
+
constraint_name: Optional[str],
|
353
353
|
columns: List[str],
|
354
354
|
) -> None:
|
355
355
|
"""Issue a "create primary key" instruction using the
|
@@ -681,7 +681,7 @@ class CreateForeignKeyOp(AddConstraintOp):
|
|
681
681
|
def batch_create_foreign_key(
|
682
682
|
cls,
|
683
683
|
operations: BatchOperations,
|
684
|
-
constraint_name: str,
|
684
|
+
constraint_name: Optional[str],
|
685
685
|
referent_table: str,
|
686
686
|
local_cols: List[str],
|
687
687
|
remote_cols: List[str],
|
@@ -1159,6 +1159,7 @@ class CreateTableOp(MigrateOperation):
|
|
1159
1159
|
columns: Sequence[SchemaItem],
|
1160
1160
|
*,
|
1161
1161
|
schema: Optional[str] = None,
|
1162
|
+
if_not_exists: Optional[bool] = None,
|
1162
1163
|
_namespace_metadata: Optional[MetaData] = None,
|
1163
1164
|
_constraints_included: bool = False,
|
1164
1165
|
**kw: Any,
|
@@ -1166,6 +1167,7 @@ class CreateTableOp(MigrateOperation):
|
|
1166
1167
|
self.table_name = table_name
|
1167
1168
|
self.columns = columns
|
1168
1169
|
self.schema = schema
|
1170
|
+
self.if_not_exists = if_not_exists
|
1169
1171
|
self.info = kw.pop("info", {})
|
1170
1172
|
self.comment = kw.pop("comment", None)
|
1171
1173
|
self.prefixes = kw.pop("prefixes", None)
|
@@ -1228,6 +1230,7 @@ class CreateTableOp(MigrateOperation):
|
|
1228
1230
|
operations: Operations,
|
1229
1231
|
table_name: str,
|
1230
1232
|
*columns: SchemaItem,
|
1233
|
+
if_not_exists: Optional[bool] = None,
|
1231
1234
|
**kw: Any,
|
1232
1235
|
) -> Table:
|
1233
1236
|
r"""Issue a "create table" instruction using the current migration
|
@@ -1300,6 +1303,10 @@ class CreateTableOp(MigrateOperation):
|
|
1300
1303
|
quoting of the schema outside of the default behavior, use
|
1301
1304
|
the SQLAlchemy construct
|
1302
1305
|
:class:`~sqlalchemy.sql.elements.quoted_name`.
|
1306
|
+
:param if_not_exists: If True, adds IF NOT EXISTS operator when
|
1307
|
+
creating the new table.
|
1308
|
+
|
1309
|
+
.. versionadded:: 1.13.3
|
1303
1310
|
:param \**kw: Other keyword arguments are passed to the underlying
|
1304
1311
|
:class:`sqlalchemy.schema.Table` object created for the command.
|
1305
1312
|
|
@@ -1307,7 +1314,7 @@ class CreateTableOp(MigrateOperation):
|
|
1307
1314
|
to the parameters given.
|
1308
1315
|
|
1309
1316
|
"""
|
1310
|
-
op = cls(table_name, columns, **kw)
|
1317
|
+
op = cls(table_name, columns, if_not_exists=if_not_exists, **kw)
|
1311
1318
|
return operations.invoke(op)
|
1312
1319
|
|
1313
1320
|
|
@@ -1320,11 +1327,13 @@ class DropTableOp(MigrateOperation):
|
|
1320
1327
|
table_name: str,
|
1321
1328
|
*,
|
1322
1329
|
schema: Optional[str] = None,
|
1330
|
+
if_exists: Optional[bool] = None,
|
1323
1331
|
table_kw: Optional[MutableMapping[Any, Any]] = None,
|
1324
1332
|
_reverse: Optional[CreateTableOp] = None,
|
1325
1333
|
) -> None:
|
1326
1334
|
self.table_name = table_name
|
1327
1335
|
self.schema = schema
|
1336
|
+
self.if_exists = if_exists
|
1328
1337
|
self.table_kw = table_kw or {}
|
1329
1338
|
self.comment = self.table_kw.pop("comment", None)
|
1330
1339
|
self.info = self.table_kw.pop("info", None)
|
@@ -1371,9 +1380,9 @@ class DropTableOp(MigrateOperation):
|
|
1371
1380
|
info=self.info.copy() if self.info else {},
|
1372
1381
|
prefixes=list(self.prefixes) if self.prefixes else [],
|
1373
1382
|
schema=self.schema,
|
1374
|
-
_constraints_included=
|
1375
|
-
|
1376
|
-
|
1383
|
+
_constraints_included=(
|
1384
|
+
self._reverse._constraints_included if self._reverse else False
|
1385
|
+
),
|
1377
1386
|
**self.table_kw,
|
1378
1387
|
)
|
1379
1388
|
return t
|
@@ -1385,6 +1394,7 @@ class DropTableOp(MigrateOperation):
|
|
1385
1394
|
table_name: str,
|
1386
1395
|
*,
|
1387
1396
|
schema: Optional[str] = None,
|
1397
|
+
if_exists: Optional[bool] = None,
|
1388
1398
|
**kw: Any,
|
1389
1399
|
) -> None:
|
1390
1400
|
r"""Issue a "drop table" instruction using the current
|
@@ -1400,11 +1410,15 @@ class DropTableOp(MigrateOperation):
|
|
1400
1410
|
quoting of the schema outside of the default behavior, use
|
1401
1411
|
the SQLAlchemy construct
|
1402
1412
|
:class:`~sqlalchemy.sql.elements.quoted_name`.
|
1413
|
+
:param if_exists: If True, adds IF EXISTS operator when
|
1414
|
+
dropping the table.
|
1415
|
+
|
1416
|
+
.. versionadded:: 1.13.3
|
1403
1417
|
:param \**kw: Other keyword arguments are passed to the underlying
|
1404
1418
|
:class:`sqlalchemy.schema.Table` object created for the command.
|
1405
1419
|
|
1406
1420
|
"""
|
1407
|
-
op = cls(table_name, schema=schema, table_kw=kw)
|
1421
|
+
op = cls(table_name, schema=schema, if_exists=if_exists, table_kw=kw)
|
1408
1422
|
operations.invoke(op)
|
1409
1423
|
|
1410
1424
|
|
alembic/operations/schemaobj.py
CHANGED
@@ -223,10 +223,12 @@ class SchemaObjects:
|
|
223
223
|
t = sa_schema.Table(name, m, *cols, **kw)
|
224
224
|
|
225
225
|
constraints = [
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
226
|
+
(
|
227
|
+
sqla_compat._copy(elem, target_table=t)
|
228
|
+
if getattr(elem, "parent", None) is not t
|
229
|
+
and getattr(elem, "parent", None) is not None
|
230
|
+
else elem
|
231
|
+
)
|
230
232
|
for elem in columns
|
231
233
|
if isinstance(elem, (Constraint, Index))
|
232
234
|
]
|
alembic/operations/toimpl.py
CHANGED
@@ -79,8 +79,14 @@ def alter_column(
|
|
79
79
|
|
80
80
|
@Operations.implementation_for(ops.DropTableOp)
|
81
81
|
def drop_table(operations: "Operations", operation: "ops.DropTableOp") -> None:
|
82
|
+
kw = {}
|
83
|
+
if operation.if_exists is not None:
|
84
|
+
if not sqla_14:
|
85
|
+
raise NotImplementedError("SQLAlchemy 1.4+ required")
|
86
|
+
|
87
|
+
kw["if_exists"] = operation.if_exists
|
82
88
|
operations.impl.drop_table(
|
83
|
-
operation.to_table(operations.migration_context)
|
89
|
+
operation.to_table(operations.migration_context), **kw
|
84
90
|
)
|
85
91
|
|
86
92
|
|
@@ -127,8 +133,14 @@ def drop_index(operations: "Operations", operation: "ops.DropIndexOp") -> None:
|
|
127
133
|
def create_table(
|
128
134
|
operations: "Operations", operation: "ops.CreateTableOp"
|
129
135
|
) -> "Table":
|
136
|
+
kw = {}
|
137
|
+
if operation.if_not_exists is not None:
|
138
|
+
if not sqla_14:
|
139
|
+
raise NotImplementedError("SQLAlchemy 1.4+ required")
|
140
|
+
|
141
|
+
kw["if_not_exists"] = operation.if_not_exists
|
130
142
|
table = operation.to_table(operations.migration_context)
|
131
|
-
operations.impl.create_table(table)
|
143
|
+
operations.impl.create_table(table, **kw)
|
132
144
|
return table
|
133
145
|
|
134
146
|
|
alembic/runtime/environment.py
CHANGED
@@ -108,7 +108,6 @@ CompareType = Callable[
|
|
108
108
|
|
109
109
|
|
110
110
|
class EnvironmentContext(util.ModuleClsProxy):
|
111
|
-
|
112
111
|
"""A configurational facade made available in an ``env.py`` script.
|
113
112
|
|
114
113
|
The :class:`.EnvironmentContext` acts as a *facade* to the more
|
@@ -342,18 +341,17 @@ class EnvironmentContext(util.ModuleClsProxy):
|
|
342
341
|
return self.context_opts.get("tag", None) # type: ignore[no-any-return] # noqa: E501
|
343
342
|
|
344
343
|
@overload
|
345
|
-
def get_x_argument(self, as_dictionary: Literal[False]) -> List[str]:
|
346
|
-
...
|
344
|
+
def get_x_argument(self, as_dictionary: Literal[False]) -> List[str]: ...
|
347
345
|
|
348
346
|
@overload
|
349
|
-
def get_x_argument(
|
350
|
-
|
347
|
+
def get_x_argument(
|
348
|
+
self, as_dictionary: Literal[True]
|
349
|
+
) -> Dict[str, str]: ...
|
351
350
|
|
352
351
|
@overload
|
353
352
|
def get_x_argument(
|
354
353
|
self, as_dictionary: bool = ...
|
355
|
-
) -> Union[List[str], Dict[str, str]]:
|
356
|
-
...
|
354
|
+
) -> Union[List[str], Dict[str, str]]: ...
|
357
355
|
|
358
356
|
def get_x_argument(
|
359
357
|
self, as_dictionary: bool = False
|
alembic/runtime/migration.py
CHANGED
@@ -86,7 +86,6 @@ class _ProxyTransaction:
|
|
86
86
|
|
87
87
|
|
88
88
|
class MigrationContext:
|
89
|
-
|
90
89
|
"""Represent the database state made available to a migration
|
91
90
|
script.
|
92
91
|
|
@@ -218,9 +217,11 @@ class MigrationContext:
|
|
218
217
|
log.info("Generating static SQL")
|
219
218
|
log.info(
|
220
219
|
"Will assume %s DDL.",
|
221
|
-
|
222
|
-
|
223
|
-
|
220
|
+
(
|
221
|
+
"transactional"
|
222
|
+
if self.impl.transactional_ddl
|
223
|
+
else "non-transactional"
|
224
|
+
),
|
224
225
|
)
|
225
226
|
|
226
227
|
@classmethod
|
@@ -345,9 +346,9 @@ class MigrationContext:
|
|
345
346
|
# except that it will not know it's in "autocommit" and will
|
346
347
|
# emit deprecation warnings when an autocommit action takes
|
347
348
|
# place.
|
348
|
-
self.connection = (
|
349
|
-
|
350
|
-
)
|
349
|
+
self.connection = self.impl.connection = (
|
350
|
+
base_connection.execution_options(isolation_level="AUTOCOMMIT")
|
351
|
+
)
|
351
352
|
|
352
353
|
# sqlalchemy future mode will "autobegin" in any case, so take
|
353
354
|
# control of that "transaction" here
|
@@ -1006,8 +1007,7 @@ class MigrationStep:
|
|
1006
1007
|
if TYPE_CHECKING:
|
1007
1008
|
|
1008
1009
|
@property
|
1009
|
-
def doc(self) -> Optional[str]:
|
1010
|
-
...
|
1010
|
+
def doc(self) -> Optional[str]: ...
|
1011
1011
|
|
1012
1012
|
@property
|
1013
1013
|
def name(self) -> str:
|
alembic/script/base.py
CHANGED
@@ -56,7 +56,6 @@ _split_on_space_comma_colon = re.compile(r", *|(?: +)|\:")
|
|
56
56
|
|
57
57
|
|
58
58
|
class ScriptDirectory:
|
59
|
-
|
60
59
|
"""Provides operations upon an Alembic script directory.
|
61
60
|
|
62
61
|
This object is useful to get information as to current revisions,
|
@@ -188,6 +187,7 @@ class ScriptDirectory:
|
|
188
187
|
split_on_path = {
|
189
188
|
None: None,
|
190
189
|
"space": " ",
|
190
|
+
"newline": "\n",
|
191
191
|
"os": os.pathsep,
|
192
192
|
":": ":",
|
193
193
|
";": ";",
|
@@ -201,7 +201,8 @@ class ScriptDirectory:
|
|
201
201
|
raise ValueError(
|
202
202
|
"'%s' is not a valid value for "
|
203
203
|
"version_path_separator; "
|
204
|
-
"expected 'space', 'os', ':', ';'"
|
204
|
+
"expected 'space', 'newline', 'os', ':', ';'"
|
205
|
+
% version_path_separator
|
205
206
|
) from ke
|
206
207
|
else:
|
207
208
|
if split_char is None:
|
@@ -211,7 +212,9 @@ class ScriptDirectory:
|
|
211
212
|
)
|
212
213
|
else:
|
213
214
|
version_locations = [
|
214
|
-
x
|
215
|
+
x.strip()
|
216
|
+
for x in version_locations_str.split(split_char)
|
217
|
+
if x
|
215
218
|
]
|
216
219
|
else:
|
217
220
|
version_locations = None
|
@@ -610,7 +613,7 @@ class ScriptDirectory:
|
|
610
613
|
if self.timezone is not None:
|
611
614
|
if ZoneInfo is None:
|
612
615
|
raise util.CommandError(
|
613
|
-
"Python >= 3.9 is required for timezone support or"
|
616
|
+
"Python >= 3.9 is required for timezone support or "
|
614
617
|
"the 'backports.zoneinfo' package must be installed."
|
615
618
|
)
|
616
619
|
# First, assume correct capitalization
|
@@ -732,9 +735,11 @@ class ScriptDirectory:
|
|
732
735
|
if depends_on:
|
733
736
|
with self._catch_revision_errors():
|
734
737
|
resolved_depends_on = [
|
735
|
-
|
736
|
-
|
737
|
-
|
738
|
+
(
|
739
|
+
dep
|
740
|
+
if dep in rev.branch_labels # maintain branch labels
|
741
|
+
else rev.revision
|
742
|
+
) # resolve partial revision identifiers
|
738
743
|
for rev, dep in [
|
739
744
|
(not_none(self.revision_map.get_revision(dep)), dep)
|
740
745
|
for dep in util.to_list(depends_on)
|
@@ -808,7 +813,6 @@ class ScriptDirectory:
|
|
808
813
|
|
809
814
|
|
810
815
|
class Script(revision.Revision):
|
811
|
-
|
812
816
|
"""Represent a single revision file in a ``versions/`` directory.
|
813
817
|
|
814
818
|
The :class:`.Script` instance is returned by methods
|
@@ -930,9 +934,11 @@ class Script(revision.Revision):
|
|
930
934
|
if head_indicators or tree_indicators:
|
931
935
|
text += "%s%s%s" % (
|
932
936
|
" (head)" if self._is_real_head else "",
|
933
|
-
|
934
|
-
|
935
|
-
|
937
|
+
(
|
938
|
+
" (effective head)"
|
939
|
+
if self.is_head and not self._is_real_head
|
940
|
+
else ""
|
941
|
+
),
|
936
942
|
" (current)" if self._db_current_indicator else "",
|
937
943
|
)
|
938
944
|
if tree_indicators:
|
alembic/script/revision.py
CHANGED
@@ -56,8 +56,7 @@ class _CollectRevisionsProtocol(Protocol):
|
|
56
56
|
inclusive: bool,
|
57
57
|
implicit_base: bool,
|
58
58
|
assert_relative_length: bool,
|
59
|
-
) -> Tuple[Set[Revision], Tuple[Optional[_RevisionOrBase], ...]]:
|
60
|
-
...
|
59
|
+
) -> Tuple[Set[Revision], Tuple[Optional[_RevisionOrBase], ...]]: ...
|
61
60
|
|
62
61
|
|
63
62
|
class RevisionError(Exception):
|
@@ -720,9 +719,11 @@ class RevisionMap:
|
|
720
719
|
resolved_target = target
|
721
720
|
|
722
721
|
resolved_test_against_revs = [
|
723
|
-
|
724
|
-
|
725
|
-
|
722
|
+
(
|
723
|
+
self._revision_for_ident(test_against_rev)
|
724
|
+
if not isinstance(test_against_rev, Revision)
|
725
|
+
else test_against_rev
|
726
|
+
)
|
726
727
|
for test_against_rev in util.to_tuple(
|
727
728
|
test_against_revs, default=()
|
728
729
|
)
|
@@ -1016,9 +1017,9 @@ class RevisionMap:
|
|
1016
1017
|
# each time but it was getting complicated
|
1017
1018
|
current_heads[current_candidate_idx] = heads_to_add[0]
|
1018
1019
|
current_heads.extend(heads_to_add[1:])
|
1019
|
-
ancestors_by_idx[
|
1020
|
-
|
1021
|
-
|
1020
|
+
ancestors_by_idx[current_candidate_idx] = (
|
1021
|
+
get_ancestors(heads_to_add[0])
|
1022
|
+
)
|
1022
1023
|
ancestors_by_idx.extend(
|
1023
1024
|
get_ancestors(head) for head in heads_to_add[1:]
|
1024
1025
|
)
|
@@ -1183,9 +1184,13 @@ class RevisionMap:
|
|
1183
1184
|
branch_label = symbol
|
1184
1185
|
# Walk down the tree to find downgrade target.
|
1185
1186
|
rev = self._walk(
|
1186
|
-
start=
|
1187
|
-
|
1188
|
-
|
1187
|
+
start=(
|
1188
|
+
self.get_revision(symbol)
|
1189
|
+
if branch_label is None
|
1190
|
+
else self.get_revision(
|
1191
|
+
"%s@%s" % (branch_label, symbol)
|
1192
|
+
)
|
1193
|
+
),
|
1189
1194
|
steps=rel_int,
|
1190
1195
|
no_overwalk=assert_relative_length,
|
1191
1196
|
)
|
@@ -1303,9 +1308,13 @@ class RevisionMap:
|
|
1303
1308
|
)
|
1304
1309
|
return (
|
1305
1310
|
self._walk(
|
1306
|
-
start=
|
1307
|
-
|
1308
|
-
|
1311
|
+
start=(
|
1312
|
+
self.get_revision(symbol)
|
1313
|
+
if branch_label is None
|
1314
|
+
else self.get_revision(
|
1315
|
+
"%s@%s" % (branch_label, symbol)
|
1316
|
+
)
|
1317
|
+
),
|
1309
1318
|
steps=relative,
|
1310
1319
|
no_overwalk=assert_relative_length,
|
1311
1320
|
),
|
@@ -1694,15 +1703,13 @@ class Revision:
|
|
1694
1703
|
|
1695
1704
|
|
1696
1705
|
@overload
|
1697
|
-
def tuple_rev_as_scalar(rev: None) -> None:
|
1698
|
-
...
|
1706
|
+
def tuple_rev_as_scalar(rev: None) -> None: ...
|
1699
1707
|
|
1700
1708
|
|
1701
1709
|
@overload
|
1702
1710
|
def tuple_rev_as_scalar(
|
1703
1711
|
rev: Union[Tuple[_T, ...], List[_T]]
|
1704
|
-
) -> Union[_T, Tuple[_T, ...], List[_T]]:
|
1705
|
-
...
|
1712
|
+
) -> Union[_T, Tuple[_T, ...], List[_T]]: ...
|
1706
1713
|
|
1707
1714
|
|
1708
1715
|
def tuple_rev_as_scalar(
|
@@ -1,7 +1,8 @@
|
|
1
1
|
# A generic, single database configuration.
|
2
2
|
|
3
3
|
[alembic]
|
4
|
-
# path to migration scripts
|
4
|
+
# path to migration scripts.
|
5
|
+
# Use forward slashes (/) also on windows to provide an os agnostic path
|
5
6
|
script_location = ${script_location}
|
6
7
|
|
7
8
|
# template used to generate migration file names; The default value is %%(rev)s_%%(slug)s
|
@@ -20,8 +21,7 @@ prepend_sys_path = .
|
|
20
21
|
# leave blank for localtime
|
21
22
|
# timezone =
|
22
23
|
|
23
|
-
# max length of characters to apply to the
|
24
|
-
# "slug" field
|
24
|
+
# max length of characters to apply to the "slug" field
|
25
25
|
# truncate_slug_length = 40
|
26
26
|
|
27
27
|
# set to 'true' to run the environment during
|
@@ -47,6 +47,7 @@ prepend_sys_path = .
|
|
47
47
|
# version_path_separator = :
|
48
48
|
# version_path_separator = ;
|
49
49
|
# version_path_separator = space
|
50
|
+
# version_path_separator = newline
|
50
51
|
version_path_separator = os # Use os.pathsep. Default configuration used for new projects.
|
51
52
|
|
52
53
|
# set to 'true' to search source files recursively
|
@@ -2,6 +2,7 @@
|
|
2
2
|
|
3
3
|
[alembic]
|
4
4
|
# path to migration scripts
|
5
|
+
# Use forward slashes (/) also on windows to provide an os agnostic path
|
5
6
|
script_location = ${script_location}
|
6
7
|
|
7
8
|
# template used to generate migration file names; The default value is %%(rev)s_%%(slug)s
|
@@ -22,8 +23,7 @@ prepend_sys_path = .
|
|
22
23
|
# leave blank for localtime
|
23
24
|
# timezone =
|
24
25
|
|
25
|
-
# max length of characters to apply to the
|
26
|
-
# "slug" field
|
26
|
+
# max length of characters to apply to the "slug" field
|
27
27
|
# truncate_slug_length = 40
|
28
28
|
|
29
29
|
# set to 'true' to run the environment during
|
@@ -49,6 +49,7 @@ prepend_sys_path = .
|
|
49
49
|
# version_path_separator = :
|
50
50
|
# version_path_separator = ;
|
51
51
|
# version_path_separator = space
|
52
|
+
# version_path_separator = newline
|
52
53
|
version_path_separator = os # Use os.pathsep. Default configuration used for new projects.
|
53
54
|
|
54
55
|
# set to 'true' to search source files recursively
|
@@ -2,6 +2,7 @@
|
|
2
2
|
|
3
3
|
[alembic]
|
4
4
|
# path to migration scripts
|
5
|
+
# Use forward slashes (/) also on windows to provide an os agnostic path
|
5
6
|
script_location = ${script_location}
|
6
7
|
|
7
8
|
# template used to generate migration file names; The default value is %%(rev)s_%%(slug)s
|
@@ -22,8 +23,7 @@ prepend_sys_path = .
|
|
22
23
|
# leave blank for localtime
|
23
24
|
# timezone =
|
24
25
|
|
25
|
-
# max length of characters to apply to the
|
26
|
-
# "slug" field
|
26
|
+
# max length of characters to apply to the "slug" field
|
27
27
|
# truncate_slug_length = 40
|
28
28
|
|
29
29
|
# set to 'true' to run the environment during
|
@@ -49,6 +49,7 @@ prepend_sys_path = .
|
|
49
49
|
# version_path_separator = :
|
50
50
|
# version_path_separator = ;
|
51
51
|
# version_path_separator = space
|
52
|
+
# version_path_separator = newline
|
52
53
|
version_path_separator = os # Use os.pathsep. Default configuration used for new projects.
|
53
54
|
|
54
55
|
# set to 'true' to search source files recursively
|
alembic/testing/assertions.py
CHANGED
@@ -74,7 +74,9 @@ class _ErrorContainer:
|
|
74
74
|
|
75
75
|
|
76
76
|
@contextlib.contextmanager
|
77
|
-
def _expect_raises(
|
77
|
+
def _expect_raises(
|
78
|
+
except_cls, msg=None, check_context=False, text_exact=False
|
79
|
+
):
|
78
80
|
ec = _ErrorContainer()
|
79
81
|
if check_context:
|
80
82
|
are_we_already_in_a_traceback = sys.exc_info()[0]
|
@@ -85,7 +87,10 @@ def _expect_raises(except_cls, msg=None, check_context=False):
|
|
85
87
|
ec.error = err
|
86
88
|
success = True
|
87
89
|
if msg is not None:
|
88
|
-
|
90
|
+
if text_exact:
|
91
|
+
assert str(err) == msg, f"{msg} != {err}"
|
92
|
+
else:
|
93
|
+
assert re.search(msg, str(err), re.UNICODE), f"{msg} !~ {err}"
|
89
94
|
if check_context and not are_we_already_in_a_traceback:
|
90
95
|
_assert_proper_exception_context(err)
|
91
96
|
print(str(err).encode("utf-8"))
|
@@ -98,8 +103,12 @@ def expect_raises(except_cls, check_context=True):
|
|
98
103
|
return _expect_raises(except_cls, check_context=check_context)
|
99
104
|
|
100
105
|
|
101
|
-
def expect_raises_message(
|
102
|
-
|
106
|
+
def expect_raises_message(
|
107
|
+
except_cls, msg, check_context=True, text_exact=False
|
108
|
+
):
|
109
|
+
return _expect_raises(
|
110
|
+
except_cls, msg=msg, check_context=check_context, text_exact=text_exact
|
111
|
+
)
|
103
112
|
|
104
113
|
|
105
114
|
def eq_ignore_whitespace(a, b, msg=None):
|
alembic/testing/fixtures.py
CHANGED
@@ -49,6 +49,12 @@ class TestBase(SQLAlchemyTestBase):
|
|
49
49
|
connection, opts=dict(transaction_per_migration=True)
|
50
50
|
)
|
51
51
|
|
52
|
+
@testing.fixture
|
53
|
+
def as_sql_migration_context(self, connection):
|
54
|
+
return MigrationContext.configure(
|
55
|
+
connection, opts=dict(transaction_per_migration=True, as_sql=True)
|
56
|
+
)
|
57
|
+
|
52
58
|
@testing.fixture
|
53
59
|
def connection(self):
|
54
60
|
with config.db.connect() as conn:
|
@@ -268,9 +274,11 @@ class AlterColRoundTripFixture:
|
|
268
274
|
"x",
|
269
275
|
column.name,
|
270
276
|
existing_type=column.type,
|
271
|
-
existing_server_default=
|
272
|
-
|
273
|
-
|
277
|
+
existing_server_default=(
|
278
|
+
column.server_default
|
279
|
+
if column.server_default is not None
|
280
|
+
else False
|
281
|
+
),
|
274
282
|
existing_nullable=True if column.nullable else False,
|
275
283
|
# existing_comment=column.comment,
|
276
284
|
nullable=to_.get("nullable", None),
|
@@ -298,9 +306,13 @@ class AlterColRoundTripFixture:
|
|
298
306
|
new_col["type"],
|
299
307
|
new_col.get("default", None),
|
300
308
|
compare.get("type", old_col["type"]),
|
301
|
-
|
302
|
-
|
303
|
-
|
304
|
-
|
305
|
-
|
309
|
+
(
|
310
|
+
compare["server_default"].text
|
311
|
+
if "server_default" in compare
|
312
|
+
else (
|
313
|
+
column.server_default.arg.text
|
314
|
+
if column.server_default is not None
|
315
|
+
else None
|
316
|
+
)
|
317
|
+
),
|
306
318
|
)
|
@@ -124,6 +124,7 @@ class AutogenerateComputedTest(AutogenFixtureTest, TestBase):
|
|
124
124
|
lambda: (None, None),
|
125
125
|
lambda: (sa.Computed("5"), sa.Computed("5")),
|
126
126
|
lambda: (sa.Computed("bar*5"), sa.Computed("bar*5")),
|
127
|
+
lambda: (sa.Computed("bar*5"), sa.Computed("bar * \r\n\t5")),
|
127
128
|
(
|
128
129
|
lambda: (sa.Computed("bar*5"), None),
|
129
130
|
config.requirements.computed_doesnt_reflect_as_server_default,
|