alembic 1.12.1__py3-none-any.whl → 1.13.1__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 -3
- alembic/autogenerate/__init__.py +10 -10
- alembic/autogenerate/api.py +8 -5
- alembic/autogenerate/compare.py +134 -199
- alembic/autogenerate/render.py +39 -24
- alembic/autogenerate/rewriter.py +26 -13
- alembic/command.py +7 -2
- alembic/config.py +20 -9
- alembic/context.pyi +12 -6
- alembic/ddl/__init__.py +1 -1
- alembic/ddl/_autogen.py +325 -0
- alembic/ddl/base.py +12 -9
- alembic/ddl/impl.py +110 -13
- alembic/ddl/mssql.py +4 -1
- alembic/ddl/mysql.py +9 -6
- alembic/ddl/oracle.py +4 -1
- alembic/ddl/postgresql.py +147 -73
- alembic/ddl/sqlite.py +8 -6
- alembic/op.pyi +46 -8
- alembic/operations/base.py +70 -14
- alembic/operations/batch.py +7 -8
- alembic/operations/ops.py +53 -31
- alembic/operations/schemaobj.py +5 -4
- alembic/operations/toimpl.py +8 -5
- alembic/runtime/environment.py +17 -7
- alembic/runtime/migration.py +27 -11
- alembic/script/base.py +34 -27
- alembic/script/revision.py +30 -17
- alembic/script/write_hooks.py +3 -0
- alembic/templates/async/alembic.ini.mako +3 -3
- alembic/templates/generic/alembic.ini.mako +3 -3
- alembic/templates/multidb/alembic.ini.mako +3 -3
- alembic/testing/requirements.py +12 -0
- alembic/testing/schemacompare.py +9 -0
- alembic/util/__init__.py +31 -31
- alembic/util/compat.py +25 -9
- alembic/util/langhelpers.py +78 -33
- alembic/util/messaging.py +6 -3
- alembic/util/pyfiles.py +7 -3
- alembic/util/sqla_compat.py +52 -26
- {alembic-1.12.1.dist-info → alembic-1.13.1.dist-info}/METADATA +5 -4
- alembic-1.13.1.dist-info/RECORD +83 -0
- {alembic-1.12.1.dist-info → alembic-1.13.1.dist-info}/WHEEL +1 -1
- alembic-1.12.1.dist-info/RECORD +0 -82
- {alembic-1.12.1.dist-info → alembic-1.13.1.dist-info}/LICENSE +0 -0
- {alembic-1.12.1.dist-info → alembic-1.13.1.dist-info}/entry_points.txt +0 -0
- {alembic-1.12.1.dist-info → alembic-1.13.1.dist-info}/top_level.txt +0 -0
alembic/autogenerate/render.py
CHANGED
@@ -1,3 +1,6 @@
|
|
1
|
+
# mypy: allow-untyped-defs, allow-incomplete-defs, allow-untyped-calls
|
2
|
+
# mypy: no-warn-return-any, allow-any-generics
|
3
|
+
|
1
4
|
from __future__ import annotations
|
2
5
|
|
3
6
|
from io import StringIO
|
@@ -164,14 +167,22 @@ def _render_modify_table(
|
|
164
167
|
def _render_create_table_comment(
|
165
168
|
autogen_context: AutogenContext, op: ops.CreateTableCommentOp
|
166
169
|
) -> str:
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
170
|
+
if autogen_context._has_batch:
|
171
|
+
templ = (
|
172
|
+
"{prefix}create_table_comment(\n"
|
173
|
+
"{indent}{comment},\n"
|
174
|
+
"{indent}existing_comment={existing}\n"
|
175
|
+
")"
|
176
|
+
)
|
177
|
+
else:
|
178
|
+
templ = (
|
179
|
+
"{prefix}create_table_comment(\n"
|
180
|
+
"{indent}'{tname}',\n"
|
181
|
+
"{indent}{comment},\n"
|
182
|
+
"{indent}existing_comment={existing},\n"
|
183
|
+
"{indent}schema={schema}\n"
|
184
|
+
")"
|
185
|
+
)
|
175
186
|
return templ.format(
|
176
187
|
prefix=_alembic_autogenerate_prefix(autogen_context),
|
177
188
|
tname=op.table_name,
|
@@ -188,13 +199,20 @@ def _render_create_table_comment(
|
|
188
199
|
def _render_drop_table_comment(
|
189
200
|
autogen_context: AutogenContext, op: ops.DropTableCommentOp
|
190
201
|
) -> str:
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
202
|
+
if autogen_context._has_batch:
|
203
|
+
templ = (
|
204
|
+
"{prefix}drop_table_comment(\n"
|
205
|
+
"{indent}existing_comment={existing}\n"
|
206
|
+
")"
|
207
|
+
)
|
208
|
+
else:
|
209
|
+
templ = (
|
210
|
+
"{prefix}drop_table_comment(\n"
|
211
|
+
"{indent}'{tname}',\n"
|
212
|
+
"{indent}existing_comment={existing},\n"
|
213
|
+
"{indent}schema={schema}\n"
|
214
|
+
")"
|
215
|
+
)
|
198
216
|
return templ.format(
|
199
217
|
prefix=_alembic_autogenerate_prefix(autogen_context),
|
200
218
|
tname=op.table_name,
|
@@ -834,7 +852,7 @@ def _render_Variant_type(
|
|
834
852
|
) -> str:
|
835
853
|
base_type, variant_mapping = sqla_compat._get_variant_mapping(type_)
|
836
854
|
base = _repr_type(base_type, autogen_context, _skip_variants=True)
|
837
|
-
assert base is not None and base is not False
|
855
|
+
assert base is not None and base is not False # type: ignore[comparison-overlap] # noqa:E501
|
838
856
|
for dialect in sorted(variant_mapping):
|
839
857
|
typ = variant_mapping[dialect]
|
840
858
|
base += ".with_variant(%s, %r)" % (
|
@@ -931,7 +949,7 @@ def _fk_colspec(
|
|
931
949
|
won't fail if the remote table can't be resolved.
|
932
950
|
|
933
951
|
"""
|
934
|
-
colspec = fk._get_colspec()
|
952
|
+
colspec = fk._get_colspec()
|
935
953
|
tokens = colspec.split(".")
|
936
954
|
tname, colname = tokens[-2:]
|
937
955
|
|
@@ -1001,8 +1019,7 @@ def _render_foreign_key(
|
|
1001
1019
|
% {
|
1002
1020
|
"prefix": _sqlalchemy_autogenerate_prefix(autogen_context),
|
1003
1021
|
"cols": ", ".join(
|
1004
|
-
|
1005
|
-
for f in constraint.elements
|
1022
|
+
repr(_ident(f.parent.name)) for f in constraint.elements
|
1006
1023
|
),
|
1007
1024
|
"refcols": ", ".join(
|
1008
1025
|
repr(_fk_colspec(f, apply_metadata_schema, namespace_metadata))
|
@@ -1043,12 +1060,10 @@ def _render_check_constraint(
|
|
1043
1060
|
# ideally SQLAlchemy would give us more of a first class
|
1044
1061
|
# way to detect this.
|
1045
1062
|
if (
|
1046
|
-
constraint._create_rule
|
1047
|
-
and hasattr(
|
1048
|
-
constraint._create_rule, "target" # type:ignore[attr-defined]
|
1049
|
-
)
|
1063
|
+
constraint._create_rule
|
1064
|
+
and hasattr(constraint._create_rule, "target")
|
1050
1065
|
and isinstance(
|
1051
|
-
constraint._create_rule.target,
|
1066
|
+
constraint._create_rule.target,
|
1052
1067
|
sqltypes.TypeEngine,
|
1053
1068
|
)
|
1054
1069
|
):
|
alembic/autogenerate/rewriter.py
CHANGED
@@ -4,7 +4,7 @@ from typing import Any
|
|
4
4
|
from typing import Callable
|
5
5
|
from typing import Iterator
|
6
6
|
from typing import List
|
7
|
-
from typing import
|
7
|
+
from typing import Tuple
|
8
8
|
from typing import Type
|
9
9
|
from typing import TYPE_CHECKING
|
10
10
|
from typing import Union
|
@@ -16,12 +16,18 @@ if TYPE_CHECKING:
|
|
16
16
|
from ..operations.ops import AddColumnOp
|
17
17
|
from ..operations.ops import AlterColumnOp
|
18
18
|
from ..operations.ops import CreateTableOp
|
19
|
+
from ..operations.ops import DowngradeOps
|
19
20
|
from ..operations.ops import MigrateOperation
|
20
21
|
from ..operations.ops import MigrationScript
|
21
22
|
from ..operations.ops import ModifyTableOps
|
22
23
|
from ..operations.ops import OpContainer
|
23
|
-
from ..
|
24
|
+
from ..operations.ops import UpgradeOps
|
24
25
|
from ..runtime.migration import MigrationContext
|
26
|
+
from ..script.revision import _GetRevArg
|
27
|
+
|
28
|
+
ProcessRevisionDirectiveFn = Callable[
|
29
|
+
["MigrationContext", "_GetRevArg", List["MigrationScript"]], None
|
30
|
+
]
|
25
31
|
|
26
32
|
|
27
33
|
class Rewriter:
|
@@ -52,15 +58,21 @@ class Rewriter:
|
|
52
58
|
|
53
59
|
_traverse = util.Dispatcher()
|
54
60
|
|
55
|
-
_chained:
|
61
|
+
_chained: Tuple[Union[ProcessRevisionDirectiveFn, Rewriter], ...] = ()
|
56
62
|
|
57
63
|
def __init__(self) -> None:
|
58
64
|
self.dispatch = util.Dispatcher()
|
59
65
|
|
60
|
-
def chain(
|
66
|
+
def chain(
|
67
|
+
self,
|
68
|
+
other: Union[
|
69
|
+
ProcessRevisionDirectiveFn,
|
70
|
+
Rewriter,
|
71
|
+
],
|
72
|
+
) -> Rewriter:
|
61
73
|
"""Produce a "chain" of this :class:`.Rewriter` to another.
|
62
74
|
|
63
|
-
This allows two rewriters to operate serially on a stream,
|
75
|
+
This allows two or more rewriters to operate serially on a stream,
|
64
76
|
e.g.::
|
65
77
|
|
66
78
|
writer1 = autogenerate.Rewriter()
|
@@ -89,7 +101,7 @@ class Rewriter:
|
|
89
101
|
"""
|
90
102
|
wr = self.__class__.__new__(self.__class__)
|
91
103
|
wr.__dict__.update(self.__dict__)
|
92
|
-
wr._chained
|
104
|
+
wr._chained += (other,)
|
93
105
|
return wr
|
94
106
|
|
95
107
|
def rewrites(
|
@@ -101,7 +113,7 @@ class Rewriter:
|
|
101
113
|
Type[CreateTableOp],
|
102
114
|
Type[ModifyTableOps],
|
103
115
|
],
|
104
|
-
) -> Callable:
|
116
|
+
) -> Callable[..., Any]:
|
105
117
|
"""Register a function as rewriter for a given type.
|
106
118
|
|
107
119
|
The function should receive three arguments, which are
|
@@ -146,8 +158,8 @@ class Rewriter:
|
|
146
158
|
directives: List[MigrationScript],
|
147
159
|
) -> None:
|
148
160
|
self.process_revision_directives(context, revision, directives)
|
149
|
-
|
150
|
-
|
161
|
+
for process_revision_directives in self._chained:
|
162
|
+
process_revision_directives(context, revision, directives)
|
151
163
|
|
152
164
|
@_traverse.dispatch_for(ops.MigrationScript)
|
153
165
|
def _traverse_script(
|
@@ -156,7 +168,7 @@ class Rewriter:
|
|
156
168
|
revision: _GetRevArg,
|
157
169
|
directive: MigrationScript,
|
158
170
|
) -> None:
|
159
|
-
upgrade_ops_list = []
|
171
|
+
upgrade_ops_list: List[UpgradeOps] = []
|
160
172
|
for upgrade_ops in directive.upgrade_ops_list:
|
161
173
|
ret = self._traverse_for(context, revision, upgrade_ops)
|
162
174
|
if len(ret) != 1:
|
@@ -164,9 +176,10 @@ class Rewriter:
|
|
164
176
|
"Can only return single object for UpgradeOps traverse"
|
165
177
|
)
|
166
178
|
upgrade_ops_list.append(ret[0])
|
167
|
-
directive.upgrade_ops = upgrade_ops_list
|
168
179
|
|
169
|
-
|
180
|
+
directive.upgrade_ops = upgrade_ops_list # type: ignore
|
181
|
+
|
182
|
+
downgrade_ops_list: List[DowngradeOps] = []
|
170
183
|
for downgrade_ops in directive.downgrade_ops_list:
|
171
184
|
ret = self._traverse_for(context, revision, downgrade_ops)
|
172
185
|
if len(ret) != 1:
|
@@ -174,7 +187,7 @@ class Rewriter:
|
|
174
187
|
"Can only return single object for DowngradeOps traverse"
|
175
188
|
)
|
176
189
|
downgrade_ops_list.append(ret[0])
|
177
|
-
directive.downgrade_ops = downgrade_ops_list
|
190
|
+
directive.downgrade_ops = downgrade_ops_list # type: ignore
|
178
191
|
|
179
192
|
@_traverse.dispatch_for(ops.OpContainer)
|
180
193
|
def _traverse_op_container(
|
alembic/command.py
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# mypy: allow-untyped-defs, allow-untyped-calls
|
2
|
+
|
1
3
|
from __future__ import annotations
|
2
4
|
|
3
5
|
import os
|
@@ -18,7 +20,7 @@ if TYPE_CHECKING:
|
|
18
20
|
from .runtime.environment import ProcessRevisionDirectiveFn
|
19
21
|
|
20
22
|
|
21
|
-
def list_templates(config: Config):
|
23
|
+
def list_templates(config: Config) -> None:
|
22
24
|
"""List available templates.
|
23
25
|
|
24
26
|
:param config: a :class:`.Config` object.
|
@@ -290,7 +292,10 @@ def check(config: "Config") -> None:
|
|
290
292
|
# the revision_context now has MigrationScript structure(s) present.
|
291
293
|
|
292
294
|
migration_script = revision_context.generated_revisions[-1]
|
293
|
-
diffs =
|
295
|
+
diffs = []
|
296
|
+
for upgrade_ops in migration_script.upgrade_ops_list:
|
297
|
+
diffs.extend(upgrade_ops.as_diffs())
|
298
|
+
|
294
299
|
if diffs:
|
295
300
|
raise util.AutogenerateDiffsDetected(
|
296
301
|
f"New upgrade operations detected: {diffs}"
|
alembic/config.py
CHANGED
@@ -12,6 +12,7 @@ from typing import Dict
|
|
12
12
|
from typing import Mapping
|
13
13
|
from typing import Optional
|
14
14
|
from typing import overload
|
15
|
+
from typing import Sequence
|
15
16
|
from typing import TextIO
|
16
17
|
from typing import Union
|
17
18
|
|
@@ -104,7 +105,7 @@ class Config:
|
|
104
105
|
stdout: TextIO = sys.stdout,
|
105
106
|
cmd_opts: Optional[Namespace] = None,
|
106
107
|
config_args: Mapping[str, Any] = util.immutabledict(),
|
107
|
-
attributes: Optional[
|
108
|
+
attributes: Optional[Dict[str, Any]] = None,
|
108
109
|
) -> None:
|
109
110
|
"""Construct a new :class:`.Config`"""
|
110
111
|
self.config_file_name = file_
|
@@ -140,7 +141,7 @@ class Config:
|
|
140
141
|
"""
|
141
142
|
|
142
143
|
@util.memoized_property
|
143
|
-
def attributes(self):
|
144
|
+
def attributes(self) -> Dict[str, Any]:
|
144
145
|
"""A Python dictionary for storage of additional state.
|
145
146
|
|
146
147
|
|
@@ -159,7 +160,7 @@ class Config:
|
|
159
160
|
"""
|
160
161
|
return {}
|
161
162
|
|
162
|
-
def print_stdout(self, text: str, *arg) -> None:
|
163
|
+
def print_stdout(self, text: str, *arg: Any) -> None:
|
163
164
|
"""Render a message to standard out.
|
164
165
|
|
165
166
|
When :meth:`.Config.print_stdout` is called with additional args
|
@@ -183,7 +184,7 @@ class Config:
|
|
183
184
|
util.write_outstream(self.stdout, output, "\n", **self.messaging_opts)
|
184
185
|
|
185
186
|
@util.memoized_property
|
186
|
-
def file_config(self):
|
187
|
+
def file_config(self) -> ConfigParser:
|
187
188
|
"""Return the underlying ``ConfigParser`` object.
|
188
189
|
|
189
190
|
Direct access to the .ini file is available here,
|
@@ -321,7 +322,9 @@ class Config:
|
|
321
322
|
) -> Optional[str]:
|
322
323
|
...
|
323
324
|
|
324
|
-
def get_main_option(
|
325
|
+
def get_main_option(
|
326
|
+
self, name: str, default: Optional[str] = None
|
327
|
+
) -> Optional[str]:
|
325
328
|
"""Return an option from the 'main' section of the .ini file.
|
326
329
|
|
327
330
|
This defaults to being a key from the ``[alembic]``
|
@@ -351,7 +354,9 @@ class CommandLine:
|
|
351
354
|
self._generate_args(prog)
|
352
355
|
|
353
356
|
def _generate_args(self, prog: Optional[str]) -> None:
|
354
|
-
def add_options(
|
357
|
+
def add_options(
|
358
|
+
fn: Any, parser: Any, positional: Any, kwargs: Any
|
359
|
+
) -> None:
|
355
360
|
kwargs_opts = {
|
356
361
|
"template": (
|
357
362
|
"-t",
|
@@ -554,7 +559,9 @@ class CommandLine:
|
|
554
559
|
)
|
555
560
|
subparsers = parser.add_subparsers()
|
556
561
|
|
557
|
-
positional_translations = {
|
562
|
+
positional_translations: Dict[Any, Any] = {
|
563
|
+
command.stamp: {"revision": "revisions"}
|
564
|
+
}
|
558
565
|
|
559
566
|
for fn in [getattr(command, n) for n in dir(command)]:
|
560
567
|
if (
|
@@ -609,7 +616,7 @@ class CommandLine:
|
|
609
616
|
else:
|
610
617
|
util.err(str(e), **config.messaging_opts)
|
611
618
|
|
612
|
-
def main(self, argv=None):
|
619
|
+
def main(self, argv: Optional[Sequence[str]] = None) -> None:
|
613
620
|
options = self.parser.parse_args(argv)
|
614
621
|
if not hasattr(options, "cmd"):
|
615
622
|
# see http://bugs.python.org/issue9253, argparse
|
@@ -624,7 +631,11 @@ class CommandLine:
|
|
624
631
|
self.run_cmd(cfg, options)
|
625
632
|
|
626
633
|
|
627
|
-
def main(
|
634
|
+
def main(
|
635
|
+
argv: Optional[Sequence[str]] = None,
|
636
|
+
prog: Optional[str] = None,
|
637
|
+
**kwargs: Any,
|
638
|
+
) -> None:
|
628
639
|
"""The console runner function for Alembic."""
|
629
640
|
|
630
641
|
CommandLine(prog=prog).main(argv=argv)
|
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:
|
101
|
+
target_metadata: Union[MetaData, Sequence[MetaData], None] = None,
|
101
102
|
include_name: Optional[
|
102
103
|
Callable[
|
103
104
|
[
|
@@ -159,8 +160,8 @@ def configure(
|
|
159
160
|
MigrationContext,
|
160
161
|
Column[Any],
|
161
162
|
Column[Any],
|
162
|
-
TypeEngine,
|
163
|
-
TypeEngine,
|
163
|
+
TypeEngine[Any],
|
164
|
+
TypeEngine[Any],
|
164
165
|
],
|
165
166
|
Optional[bool],
|
166
167
|
],
|
@@ -635,7 +636,8 @@ def configure(
|
|
635
636
|
"""
|
636
637
|
|
637
638
|
def execute(
|
638
|
-
sql: Union[Executable, str],
|
639
|
+
sql: Union[Executable, str],
|
640
|
+
execution_options: Optional[Dict[str, Any]] = None,
|
639
641
|
) -> None:
|
640
642
|
"""Execute the given SQL using the current change context.
|
641
643
|
|
@@ -758,7 +760,11 @@ def get_x_argument(
|
|
758
760
|
The return value is a list, returned directly from the ``argparse``
|
759
761
|
structure. If ``as_dictionary=True`` is passed, the ``x`` arguments
|
760
762
|
are parsed using ``key=value`` format into a dictionary that is
|
761
|
-
then returned.
|
763
|
+
then returned. If there is no ``=`` in the argument, value is an empty
|
764
|
+
string.
|
765
|
+
|
766
|
+
.. versionchanged:: 1.13.1 Support ``as_dictionary=True`` when
|
767
|
+
arguments are passed without the ``=`` symbol.
|
762
768
|
|
763
769
|
For example, to support passing a database URL on the command line,
|
764
770
|
the standard ``env.py`` script can be modified like this::
|
@@ -800,7 +806,7 @@ def is_offline_mode() -> bool:
|
|
800
806
|
|
801
807
|
"""
|
802
808
|
|
803
|
-
def is_transactional_ddl():
|
809
|
+
def is_transactional_ddl() -> bool:
|
804
810
|
"""Return True if the context is configured to expect a
|
805
811
|
transactional DDL capable backend.
|
806
812
|
|
alembic/ddl/__init__.py
CHANGED