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 +1 -3
- alembic/autogenerate/api.py +2 -1
- alembic/autogenerate/compare.py +129 -195
- alembic/autogenerate/render.py +30 -15
- alembic/command.py +4 -1
- alembic/context.pyi +2 -1
- alembic/ddl/_autogen.py +323 -0
- alembic/ddl/impl.py +107 -13
- alembic/ddl/mysql.py +4 -3
- alembic/ddl/postgresql.py +131 -64
- alembic/operations/ops.py +1 -1
- alembic/operations/toimpl.py +5 -5
- alembic/runtime/environment.py +2 -1
- alembic/script/base.py +22 -13
- 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/compat.py +0 -1
- alembic/util/sqla_compat.py +9 -12
- {alembic-1.12.1.dist-info → alembic-1.13.0.dist-info}/METADATA +5 -4
- {alembic-1.12.1.dist-info → alembic-1.13.0.dist-info}/RECORD +27 -26
- {alembic-1.12.1.dist-info → alembic-1.13.0.dist-info}/WHEEL +1 -1
- {alembic-1.12.1.dist-info → alembic-1.13.0.dist-info}/LICENSE +0 -0
- {alembic-1.12.1.dist-info → alembic-1.13.0.dist-info}/entry_points.txt +0 -0
- {alembic-1.12.1.dist-info → alembic-1.13.0.dist-info}/top_level.txt +0 -0
alembic/ddl/postgresql.py
CHANGED
@@ -21,10 +21,8 @@ from sqlalchemy.dialects.postgresql import BIGINT
|
|
21
21
|
from sqlalchemy.dialects.postgresql import ExcludeConstraint
|
22
22
|
from sqlalchemy.dialects.postgresql import INTEGER
|
23
23
|
from sqlalchemy.schema import CreateIndex
|
24
|
-
from sqlalchemy.sql import operators
|
25
24
|
from sqlalchemy.sql.elements import ColumnClause
|
26
25
|
from sqlalchemy.sql.elements import TextClause
|
27
|
-
from sqlalchemy.sql.elements import UnaryExpression
|
28
26
|
from sqlalchemy.sql.functions import FunctionElement
|
29
27
|
from sqlalchemy.types import NULLTYPE
|
30
28
|
|
@@ -38,6 +36,7 @@ from .base import format_table_name
|
|
38
36
|
from .base import format_type
|
39
37
|
from .base import IdentityColumnDefault
|
40
38
|
from .base import RenameTable
|
39
|
+
from .impl import ComparisonResult
|
41
40
|
from .impl import DefaultImpl
|
42
41
|
from .. import util
|
43
42
|
from ..autogenerate import render
|
@@ -252,62 +251,60 @@ class PostgresqlImpl(DefaultImpl):
|
|
252
251
|
if not sqla_compat.sqla_2:
|
253
252
|
self._skip_functional_indexes(metadata_indexes, conn_indexes)
|
254
253
|
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
|
254
|
+
# pg behavior regarding modifiers
|
255
|
+
# | # | compiled sql | returned sql | regexp. group is removed |
|
256
|
+
# | - | ---------------- | -----------------| ------------------------ |
|
257
|
+
# | 1 | nulls first | nulls first | - |
|
258
|
+
# | 2 | nulls last | | (?<! desc)( nulls last)$ |
|
259
|
+
# | 3 | asc | | ( asc)$ |
|
260
|
+
# | 4 | asc nulls first | nulls first | ( asc) nulls first$ |
|
261
|
+
# | 5 | asc nulls last | | ( asc nulls last)$ |
|
262
|
+
# | 6 | desc | desc | - |
|
263
|
+
# | 7 | desc nulls first | desc | desc( nulls first)$ |
|
264
|
+
# | 8 | desc nulls last | desc nulls last | - |
|
265
|
+
_default_modifiers_re = ( # order of case 2 and 5 matters
|
266
|
+
re.compile("( asc nulls last)$"), # case 5
|
267
|
+
re.compile("(?<! desc)( nulls last)$"), # case 2
|
268
|
+
re.compile("( asc)$"), # case 3
|
269
|
+
re.compile("( asc) nulls first$"), # case 4
|
270
|
+
re.compile(" desc( nulls first)$"), # case 7
|
271
|
+
)
|
272
|
+
|
273
|
+
def _cleanup_index_expr(self, index: Index, expr: str) -> str:
|
259
274
|
expr = expr.lower().replace('"', "").replace("'", "")
|
260
275
|
if index.table is not None:
|
261
276
|
# should not be needed, since include_table=False is in compile
|
262
277
|
expr = expr.replace(f"{index.table.name.lower()}.", "")
|
263
278
|
|
264
|
-
while expr and expr[0] == "(" and expr[-1] == ")":
|
265
|
-
expr = expr[1:-1]
|
266
279
|
if "::" in expr:
|
267
280
|
# strip :: cast. types can have spaces in them
|
268
281
|
expr = re.sub(r"(::[\w ]+\w)", "", expr)
|
269
282
|
|
270
|
-
|
271
|
-
expr = expr[
|
272
|
-
|
273
|
-
# print(f"START: {start} END: {expr}")
|
274
|
-
return expr
|
283
|
+
while expr and expr[0] == "(" and expr[-1] == ")":
|
284
|
+
expr = expr[1:-1]
|
275
285
|
|
276
|
-
|
277
|
-
|
278
|
-
|
279
|
-
if
|
280
|
-
|
281
|
-
|
282
|
-
op = exp.modifier
|
283
|
-
if isinstance(exp.element, UnaryExpression):
|
284
|
-
inner_op = exp.element.modifier
|
285
|
-
else:
|
286
|
-
inner_op = None
|
287
|
-
if inner_op is None:
|
288
|
-
if op == operators.asc_op:
|
289
|
-
# default is asc
|
290
|
-
to_remove = " asc"
|
291
|
-
elif op == operators.nullslast_op:
|
292
|
-
# default is nulls last
|
293
|
-
to_remove = " nulls last"
|
294
|
-
else:
|
295
|
-
if (
|
296
|
-
inner_op == operators.asc_op
|
297
|
-
and op == operators.nullslast_op
|
298
|
-
):
|
299
|
-
# default is asc nulls last
|
300
|
-
to_remove = " asc nulls last"
|
301
|
-
elif (
|
302
|
-
inner_op == operators.desc_op
|
303
|
-
and op == operators.nullsfirst_op
|
304
|
-
):
|
305
|
-
# default for desc is nulls first
|
306
|
-
to_remove = " nulls first"
|
286
|
+
# NOTE: when parsing the connection expression this cleanup could
|
287
|
+
# be skipped
|
288
|
+
for rs in self._default_modifiers_re:
|
289
|
+
if match := rs.search(expr):
|
290
|
+
start, end = match.span(1)
|
291
|
+
expr = expr[:start] + expr[end:]
|
307
292
|
break
|
308
|
-
return to_remove
|
309
293
|
|
310
|
-
|
294
|
+
while expr and expr[0] == "(" and expr[-1] == ")":
|
295
|
+
expr = expr[1:-1]
|
296
|
+
|
297
|
+
# strip casts
|
298
|
+
cast_re = re.compile(r"cast\s*\(")
|
299
|
+
if cast_re.match(expr):
|
300
|
+
expr = cast_re.sub("", expr)
|
301
|
+
# remove the as type
|
302
|
+
expr = re.sub(r"as\s+[^)]+\)", "", expr)
|
303
|
+
# remove spaces
|
304
|
+
expr = expr.replace(" ", "")
|
305
|
+
return expr
|
306
|
+
|
307
|
+
def _dialect_options(
|
311
308
|
self, item: Union[Index, UniqueConstraint]
|
312
309
|
) -> Tuple[Any, ...]:
|
313
310
|
# only the positive case is returned by sqlalchemy reflection so
|
@@ -316,25 +313,93 @@ class PostgresqlImpl(DefaultImpl):
|
|
316
313
|
return ("nulls_not_distinct",)
|
317
314
|
return ()
|
318
315
|
|
319
|
-
def
|
320
|
-
|
321
|
-
|
322
|
-
|
323
|
-
|
324
|
-
|
325
|
-
|
326
|
-
|
327
|
-
|
316
|
+
def compare_indexes(
|
317
|
+
self,
|
318
|
+
metadata_index: Index,
|
319
|
+
reflected_index: Index,
|
320
|
+
) -> ComparisonResult:
|
321
|
+
msg = []
|
322
|
+
unique_msg = self._compare_index_unique(
|
323
|
+
metadata_index, reflected_index
|
324
|
+
)
|
325
|
+
if unique_msg:
|
326
|
+
msg.append(unique_msg)
|
327
|
+
m_exprs = metadata_index.expressions
|
328
|
+
r_exprs = reflected_index.expressions
|
329
|
+
if len(m_exprs) != len(r_exprs):
|
330
|
+
msg.append(f"expression number {len(r_exprs)} to {len(m_exprs)}")
|
331
|
+
if msg:
|
332
|
+
# no point going further, return early
|
333
|
+
return ComparisonResult.Different(msg)
|
334
|
+
skip = []
|
335
|
+
for pos, (m_e, r_e) in enumerate(zip(m_exprs, r_exprs), 1):
|
336
|
+
m_compile = self._compile_element(m_e)
|
337
|
+
m_text = self._cleanup_index_expr(metadata_index, m_compile)
|
338
|
+
# print(f"META ORIG: {m_compile!r} CLEANUP: {m_text!r}")
|
339
|
+
r_compile = self._compile_element(r_e)
|
340
|
+
r_text = self._cleanup_index_expr(metadata_index, r_compile)
|
341
|
+
# print(f"CONN ORIG: {r_compile!r} CLEANUP: {r_text!r}")
|
342
|
+
if m_text == r_text:
|
343
|
+
continue # expressions these are equal
|
344
|
+
elif m_compile.strip().endswith("_ops") and (
|
345
|
+
" " in m_compile or ")" in m_compile # is an expression
|
346
|
+
):
|
347
|
+
skip.append(
|
348
|
+
f"expression #{pos} {m_compile!r} detected "
|
349
|
+
"as including operator clause."
|
350
|
+
)
|
351
|
+
util.warn(
|
352
|
+
f"Expression #{pos} {m_compile!r} in index "
|
353
|
+
f"{reflected_index.name!r} detected to include "
|
354
|
+
"an operator clause. Expression compare cannot proceed. "
|
355
|
+
"Please move the operator clause to the "
|
356
|
+
"``postgresql_ops`` dict to enable proper compare "
|
357
|
+
"of the index expressions: "
|
358
|
+
"https://docs.sqlalchemy.org/en/latest/dialects/postgresql.html#operator-classes", # noqa: E501
|
359
|
+
)
|
360
|
+
else:
|
361
|
+
msg.append(f"expression #{pos} {r_compile!r} to {m_compile!r}")
|
362
|
+
|
363
|
+
m_options = self._dialect_options(metadata_index)
|
364
|
+
r_options = self._dialect_options(reflected_index)
|
365
|
+
if m_options != r_options:
|
366
|
+
msg.extend(f"options {r_options} to {m_options}")
|
367
|
+
|
368
|
+
if msg:
|
369
|
+
return ComparisonResult.Different(msg)
|
370
|
+
elif skip:
|
371
|
+
# if there are other changes detected don't skip the index
|
372
|
+
return ComparisonResult.Skip(skip)
|
373
|
+
else:
|
374
|
+
return ComparisonResult.Equal()
|
375
|
+
|
376
|
+
def compare_unique_constraint(
|
377
|
+
self,
|
378
|
+
metadata_constraint: UniqueConstraint,
|
379
|
+
reflected_constraint: UniqueConstraint,
|
380
|
+
) -> ComparisonResult:
|
381
|
+
metadata_tup = self._create_metadata_constraint_sig(
|
382
|
+
metadata_constraint
|
383
|
+
)
|
384
|
+
reflected_tup = self._create_reflected_constraint_sig(
|
385
|
+
reflected_constraint
|
386
|
+
)
|
387
|
+
|
388
|
+
meta_sig = metadata_tup.unnamed
|
389
|
+
conn_sig = reflected_tup.unnamed
|
390
|
+
if conn_sig != meta_sig:
|
391
|
+
return ComparisonResult.Different(
|
392
|
+
f"expression {conn_sig} to {meta_sig}"
|
328
393
|
)
|
329
|
-
for e in index.expressions
|
330
|
-
) + self._dialect_sig(index)
|
331
394
|
|
332
|
-
|
333
|
-
self
|
334
|
-
|
335
|
-
|
336
|
-
|
337
|
-
|
395
|
+
metadata_do = self._dialect_options(metadata_tup.const)
|
396
|
+
conn_do = self._dialect_options(reflected_tup.const)
|
397
|
+
if metadata_do != conn_do:
|
398
|
+
return ComparisonResult.Different(
|
399
|
+
f"expression {conn_do} to {metadata_do}"
|
400
|
+
)
|
401
|
+
|
402
|
+
return ComparisonResult.Equal()
|
338
403
|
|
339
404
|
def adjust_reflected_dialect_options(
|
340
405
|
self, reflected_options: Dict[str, Any], kind: str
|
@@ -345,7 +410,9 @@ class PostgresqlImpl(DefaultImpl):
|
|
345
410
|
options.pop("postgresql_include", None)
|
346
411
|
return options
|
347
412
|
|
348
|
-
def _compile_element(self, element: ClauseElement) -> str:
|
413
|
+
def _compile_element(self, element: Union[ClauseElement, str]) -> str:
|
414
|
+
if isinstance(element, str):
|
415
|
+
return element
|
349
416
|
return element.compile(
|
350
417
|
dialect=self.dialect,
|
351
418
|
compile_kwargs={"literal_binds": True, "include_table": False},
|
alembic/operations/ops.py
CHANGED
@@ -899,7 +899,7 @@ class CreateIndexOp(MigrateOperation):
|
|
899
899
|
return cls(
|
900
900
|
index.name, # type: ignore[arg-type]
|
901
901
|
index.table.name,
|
902
|
-
|
902
|
+
index.expressions,
|
903
903
|
schema=index.table.schema,
|
904
904
|
unique=index.unique,
|
905
905
|
**index.kwargs,
|
alembic/operations/toimpl.py
CHANGED
@@ -5,7 +5,7 @@ from sqlalchemy import schema as sa_schema
|
|
5
5
|
from . import ops
|
6
6
|
from .base import Operations
|
7
7
|
from ..util.sqla_compat import _copy
|
8
|
-
from ..util.sqla_compat import
|
8
|
+
from ..util.sqla_compat import sqla_14
|
9
9
|
|
10
10
|
if TYPE_CHECKING:
|
11
11
|
from sqlalchemy.sql.schema import Table
|
@@ -98,8 +98,8 @@ def create_index(
|
|
98
98
|
idx = operation.to_index(operations.migration_context)
|
99
99
|
kw = {}
|
100
100
|
if operation.if_not_exists is not None:
|
101
|
-
if not
|
102
|
-
raise NotImplementedError("SQLAlchemy
|
101
|
+
if not sqla_14:
|
102
|
+
raise NotImplementedError("SQLAlchemy 1.4+ required")
|
103
103
|
|
104
104
|
kw["if_not_exists"] = operation.if_not_exists
|
105
105
|
operations.impl.create_index(idx, **kw)
|
@@ -109,8 +109,8 @@ def create_index(
|
|
109
109
|
def drop_index(operations: "Operations", operation: "ops.DropIndexOp") -> None:
|
110
110
|
kw = {}
|
111
111
|
if operation.if_exists is not None:
|
112
|
-
if not
|
113
|
-
raise NotImplementedError("SQLAlchemy
|
112
|
+
if not sqla_14:
|
113
|
+
raise NotImplementedError("SQLAlchemy 1.4+ required")
|
114
114
|
|
115
115
|
kw["if_exists"] = operation.if_exists
|
116
116
|
|
alembic/runtime/environment.py
CHANGED
@@ -10,6 +10,7 @@ from typing import Mapping
|
|
10
10
|
from typing import MutableMapping
|
11
11
|
from typing import Optional
|
12
12
|
from typing import overload
|
13
|
+
from typing import Sequence
|
13
14
|
from typing import TextIO
|
14
15
|
from typing import Tuple
|
15
16
|
from typing import TYPE_CHECKING
|
@@ -416,7 +417,7 @@ class EnvironmentContext(util.ModuleClsProxy):
|
|
416
417
|
tag: Optional[str] = None,
|
417
418
|
template_args: Optional[Dict[str, Any]] = None,
|
418
419
|
render_as_batch: bool = False,
|
419
|
-
target_metadata:
|
420
|
+
target_metadata: Union[MetaData, Sequence[MetaData], None] = None,
|
420
421
|
include_name: Optional[IncludeNameFn] = None,
|
421
422
|
include_object: Optional[IncludeObjectFn] = None,
|
422
423
|
include_schemas: bool = False,
|
alembic/script/base.py
CHANGED
@@ -23,6 +23,7 @@ from . import revision
|
|
23
23
|
from . import write_hooks
|
24
24
|
from .. import util
|
25
25
|
from ..runtime import migration
|
26
|
+
from ..util import compat
|
26
27
|
from ..util import not_none
|
27
28
|
|
28
29
|
if TYPE_CHECKING:
|
@@ -35,9 +36,14 @@ if TYPE_CHECKING:
|
|
35
36
|
from ..runtime.migration import StampStep
|
36
37
|
|
37
38
|
try:
|
38
|
-
|
39
|
+
if compat.py39:
|
40
|
+
from zoneinfo import ZoneInfo
|
41
|
+
from zoneinfo import ZoneInfoNotFoundError
|
42
|
+
else:
|
43
|
+
from backports.zoneinfo import ZoneInfo # type: ignore[import-not-found,no-redef] # noqa: E501
|
44
|
+
from backports.zoneinfo import ZoneInfoNotFoundError # type: ignore[import-not-found,no-redef] # noqa: E501
|
39
45
|
except ImportError:
|
40
|
-
|
46
|
+
ZoneInfo = None # type: ignore[assignment, misc]
|
41
47
|
|
42
48
|
_sourceless_rev_file = re.compile(r"(?!\.\#|__init__)(.*\.py)(c|o)?$")
|
43
49
|
_only_source_rev_file = re.compile(r"(?!\.\#|__init__)(.*\.py)$")
|
@@ -604,23 +610,26 @@ class ScriptDirectory:
|
|
604
610
|
|
605
611
|
def _generate_create_date(self) -> datetime.datetime:
|
606
612
|
if self.timezone is not None:
|
607
|
-
if
|
613
|
+
if ZoneInfo is None:
|
608
614
|
raise util.CommandError(
|
609
|
-
"
|
610
|
-
"
|
615
|
+
"Python >= 3.9 is required for timezone support or"
|
616
|
+
"the 'backports.zoneinfo' package must be installed."
|
611
617
|
)
|
612
618
|
# First, assume correct capitalization
|
613
|
-
|
614
|
-
|
615
|
-
|
616
|
-
tzinfo =
|
619
|
+
try:
|
620
|
+
tzinfo = ZoneInfo(self.timezone)
|
621
|
+
except ZoneInfoNotFoundError:
|
622
|
+
tzinfo = None
|
617
623
|
if tzinfo is None:
|
618
|
-
|
619
|
-
|
620
|
-
|
624
|
+
try:
|
625
|
+
tzinfo = ZoneInfo(self.timezone.upper())
|
626
|
+
except ZoneInfoNotFoundError:
|
627
|
+
raise util.CommandError(
|
628
|
+
"Can't locate timezone: %s" % self.timezone
|
629
|
+
) from None
|
621
630
|
create_date = (
|
622
631
|
datetime.datetime.utcnow()
|
623
|
-
.replace(tzinfo=
|
632
|
+
.replace(tzinfo=datetime.timezone.utc)
|
624
633
|
.astimezone(tzinfo)
|
625
634
|
)
|
626
635
|
else:
|
@@ -14,9 +14,9 @@ prepend_sys_path = .
|
|
14
14
|
|
15
15
|
# timezone to use when rendering the date within the migration file
|
16
16
|
# as well as the filename.
|
17
|
-
# If specified, requires the python
|
18
|
-
# installed by adding `alembic[tz]` to the pip requirements
|
19
|
-
# string value is passed to
|
17
|
+
# If specified, requires the python>=3.9 or backports.zoneinfo library.
|
18
|
+
# Any required deps can installed by adding `alembic[tz]` to the pip requirements
|
19
|
+
# string value is passed to ZoneInfo()
|
20
20
|
# leave blank for localtime
|
21
21
|
# timezone =
|
22
22
|
|
@@ -16,9 +16,9 @@ prepend_sys_path = .
|
|
16
16
|
|
17
17
|
# timezone to use when rendering the date within the migration file
|
18
18
|
# as well as the filename.
|
19
|
-
# If specified, requires the python
|
20
|
-
# installed by adding `alembic[tz]` to the pip requirements
|
21
|
-
# string value is passed to
|
19
|
+
# If specified, requires the python>=3.9 or backports.zoneinfo library.
|
20
|
+
# Any required deps can installed by adding `alembic[tz]` to the pip requirements
|
21
|
+
# string value is passed to ZoneInfo()
|
22
22
|
# leave blank for localtime
|
23
23
|
# timezone =
|
24
24
|
|
@@ -16,9 +16,9 @@ prepend_sys_path = .
|
|
16
16
|
|
17
17
|
# timezone to use when rendering the date within the migration file
|
18
18
|
# as well as the filename.
|
19
|
-
# If specified, requires the python
|
20
|
-
# installed by adding `alembic[tz]` to the pip requirements
|
21
|
-
# string value is passed to
|
19
|
+
# If specified, requires the python>=3.9 or backports.zoneinfo library.
|
20
|
+
# Any required deps can installed by adding `alembic[tz]` to the pip requirements
|
21
|
+
# string value is passed to ZoneInfo()
|
22
22
|
# leave blank for localtime
|
23
23
|
# timezone =
|
24
24
|
|
alembic/testing/requirements.py
CHANGED
@@ -95,6 +95,18 @@ class SuiteRequirements(Requirements):
|
|
95
95
|
"SQLAlchemy 2.x test",
|
96
96
|
)
|
97
97
|
|
98
|
+
@property
|
99
|
+
def asyncio(self):
|
100
|
+
def go(config):
|
101
|
+
try:
|
102
|
+
import greenlet # noqa: F401
|
103
|
+
except ImportError:
|
104
|
+
return False
|
105
|
+
else:
|
106
|
+
return True
|
107
|
+
|
108
|
+
return self.sqlalchemy_14 + exclusions.only_if(go)
|
109
|
+
|
98
110
|
@property
|
99
111
|
def comments(self):
|
100
112
|
return exclusions.only_if(
|
alembic/testing/schemacompare.py
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
from itertools import zip_longest
|
2
2
|
|
3
3
|
from sqlalchemy import schema
|
4
|
+
from sqlalchemy.sql.elements import ClauseList
|
4
5
|
|
5
6
|
|
6
7
|
class CompareTable:
|
@@ -60,6 +61,14 @@ class CompareIndex:
|
|
60
61
|
def __ne__(self, other):
|
61
62
|
return not self.__eq__(other)
|
62
63
|
|
64
|
+
def __repr__(self):
|
65
|
+
expr = ClauseList(*self.index.expressions)
|
66
|
+
try:
|
67
|
+
expr_str = expr.compile().string
|
68
|
+
except Exception:
|
69
|
+
expr_str = str(expr)
|
70
|
+
return f"<CompareIndex {self.index.name}({expr_str})>"
|
71
|
+
|
63
72
|
|
64
73
|
class CompareCheckConstraint:
|
65
74
|
def __init__(self, constraint):
|
alembic/util/compat.py
CHANGED
alembic/util/sqla_compat.py
CHANGED
@@ -524,14 +524,6 @@ def _render_literal_bindparam(
|
|
524
524
|
return compiler.render_literal_bindparam(element, **kw)
|
525
525
|
|
526
526
|
|
527
|
-
def _get_index_expressions(idx):
|
528
|
-
return list(idx.expressions)
|
529
|
-
|
530
|
-
|
531
|
-
def _get_index_column_names(idx):
|
532
|
-
return [getattr(exp, "name", None) for exp in _get_index_expressions(idx)]
|
533
|
-
|
534
|
-
|
535
527
|
def _column_kwargs(col: Column) -> Mapping:
|
536
528
|
if sqla_13:
|
537
529
|
return col.kwargs
|
@@ -630,10 +622,15 @@ else:
|
|
630
622
|
|
631
623
|
|
632
624
|
def is_expression_index(index: Index) -> bool:
|
633
|
-
expr: Any
|
634
625
|
for expr in index.expressions:
|
635
|
-
|
636
|
-
expr = expr.element
|
637
|
-
if not isinstance(expr, ColumnClause) or expr.is_literal:
|
626
|
+
if is_expression(expr):
|
638
627
|
return True
|
639
628
|
return False
|
629
|
+
|
630
|
+
|
631
|
+
def is_expression(expr: Any) -> bool:
|
632
|
+
while isinstance(expr, UnaryExpression):
|
633
|
+
expr = expr.element
|
634
|
+
if not isinstance(expr, ColumnClause) or expr.is_literal:
|
635
|
+
return True
|
636
|
+
return False
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: alembic
|
3
|
-
Version: 1.
|
3
|
+
Version: 1.13.0
|
4
4
|
Summary: A database migration tool for SQLAlchemy.
|
5
5
|
Home-page: https://alembic.sqlalchemy.org
|
6
6
|
Author: Mike Bayer
|
@@ -17,14 +17,15 @@ Classifier: License :: OSI Approved :: MIT License
|
|
17
17
|
Classifier: Operating System :: OS Independent
|
18
18
|
Classifier: Programming Language :: Python
|
19
19
|
Classifier: Programming Language :: Python :: 3
|
20
|
-
Classifier: Programming Language :: Python :: 3.7
|
21
20
|
Classifier: Programming Language :: Python :: 3.8
|
22
21
|
Classifier: Programming Language :: Python :: 3.9
|
23
22
|
Classifier: Programming Language :: Python :: 3.10
|
23
|
+
Classifier: Programming Language :: Python :: 3.11
|
24
|
+
Classifier: Programming Language :: Python :: 3.12
|
24
25
|
Classifier: Programming Language :: Python :: Implementation :: CPython
|
25
26
|
Classifier: Programming Language :: Python :: Implementation :: PyPy
|
26
27
|
Classifier: Topic :: Database :: Front-Ends
|
27
|
-
Requires-Python: >=3.
|
28
|
+
Requires-Python: >=3.8
|
28
29
|
Description-Content-Type: text/x-rst
|
29
30
|
License-File: LICENSE
|
30
31
|
Requires-Dist: SQLAlchemy >=1.3.0
|
@@ -33,7 +34,7 @@ Requires-Dist: typing-extensions >=4
|
|
33
34
|
Requires-Dist: importlib-metadata ; python_version < "3.9"
|
34
35
|
Requires-Dist: importlib-resources ; python_version < "3.9"
|
35
36
|
Provides-Extra: tz
|
36
|
-
Requires-Dist:
|
37
|
+
Requires-Dist: backports.zoneinfo ; (python_version < "3.9") and extra == 'tz'
|
37
38
|
|
38
39
|
Alembic is a database migrations tool written by the author
|
39
40
|
of `SQLAlchemy <http://www.sqlalchemy.org>`_. A migrations tool
|
@@ -1,58 +1,59 @@
|
|
1
|
-
alembic/__init__.py,sha256=
|
1
|
+
alembic/__init__.py,sha256=Lc3Y-2KqkUQxaG2kXWDKmrLZnxw1EsfEZHOkz0oYmUE,63
|
2
2
|
alembic/__main__.py,sha256=373m7-TBh72JqrSMYviGrxCHZo-cnweM8AGF8A22PmY,78
|
3
|
-
alembic/command.py,sha256=
|
3
|
+
alembic/command.py,sha256=lLQoMaMC1ltzo2j2WgY2yl9vA9chqYqHWbYuNe_UKqA,21655
|
4
4
|
alembic/config.py,sha256=68e1nmYU5Nfh0bNRqRWUygSilDl1p0G_U1zZ8ifgmD8,21931
|
5
5
|
alembic/context.py,sha256=hK1AJOQXJ29Bhn276GYcosxeG7pC5aZRT5E8c4bMJ4Q,195
|
6
|
-
alembic/context.pyi,sha256=
|
6
|
+
alembic/context.pyi,sha256=E3O9N14_HQ9MCQAtyGm8WX7QHV6VwA3ft9TLJspUWdo,31514
|
7
7
|
alembic/environment.py,sha256=MM5lPayGT04H3aeng1H7GQ8HEAs3VGX5yy6mDLCPLT4,43
|
8
8
|
alembic/migration.py,sha256=MV6Fju6rZtn2fTREKzXrCZM6aIBGII4OMZFix0X-GLs,41
|
9
9
|
alembic/op.py,sha256=flHtcsVqOD-ZgZKK2pv-CJ5Cwh-KJ7puMUNXzishxLw,167
|
10
10
|
alembic/op.pyi,sha256=ldQBwAfzm_-ZsC3nizMuGoD34hjMKb4V_-Q1rR8q8LI,48591
|
11
11
|
alembic/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
12
12
|
alembic/autogenerate/__init__.py,sha256=4IHgWH89pForRq-yCDZhGjjVtsfGX5ECWNPuUs8nGUk,351
|
13
|
-
alembic/autogenerate/api.py,sha256=
|
14
|
-
alembic/autogenerate/compare.py,sha256=
|
15
|
-
alembic/autogenerate/render.py,sha256=
|
13
|
+
alembic/autogenerate/api.py,sha256=SJMr8csBNv_4n9cYVLDjUNbukcVxjpLsQ4ctrDGpnR0,22012
|
14
|
+
alembic/autogenerate/compare.py,sha256=t1r0q6vXNmmrgGs7yhFP0-YqOBXmn2_EMnYJWsvezxk,44826
|
15
|
+
alembic/autogenerate/render.py,sha256=HXfwRPaaoFUXylebeN-CdGvKg4OkRV7hdc_vv3qLsa0,34942
|
16
16
|
alembic/autogenerate/rewriter.py,sha256=Osba8GFVeqiX1ypGJW7Axt0ui2EROlaFtVZdMFbhzZ0,7384
|
17
17
|
alembic/ddl/__init__.py,sha256=xXr1W6PePe0gCLwR42ude0E6iru9miUFc1fCeQN4YP8,137
|
18
|
+
alembic/ddl/_autogen.py,sha256=8HBUiYpLwi6iCGU7rmEVsPaCGeBLmEI2AcpqYkOpG9k,9086
|
18
19
|
alembic/ddl/base.py,sha256=cCY3NldMRggrKd9bZ0mFRBE9GNDaAy0UJcM3ey4Utgw,9638
|
19
|
-
alembic/ddl/impl.py,sha256=
|
20
|
+
alembic/ddl/impl.py,sha256=NoYq6CGTzavYUajjy_47LZx4di_d7Z8Niu8p0SKDn84,28620
|
20
21
|
alembic/ddl/mssql.py,sha256=0k26xnUSZNj3qCHEMzRFbaWgUzKcV07I3_-Ns47VhO0,14105
|
21
|
-
alembic/ddl/mysql.py,sha256=
|
22
|
+
alembic/ddl/mysql.py,sha256=mb0oHqTmJHQXxl3gocwK-tIkCh-H1-Mb7rFYTNbk37s,16715
|
22
23
|
alembic/ddl/oracle.py,sha256=E0VaZaUM_5mwqNiJVA3zOAK-cuHVVIv_-NmUbH1JuGQ,6097
|
23
|
-
alembic/ddl/postgresql.py,sha256=
|
24
|
+
alembic/ddl/postgresql.py,sha256=VTxqqMpdzW-yaJ1_J85GhnZSWq1vwuDbOrzJ4OtlxPo,29656
|
24
25
|
alembic/ddl/sqlite.py,sha256=9q7NAxyeFwn9kWwQSc9RLeMFSos8waM7x9lnXdByh44,7613
|
25
26
|
alembic/operations/__init__.py,sha256=e0KQSZAgLpTWvyvreB7DWg7RJV_MWSOPVDgCqsd2FzY,318
|
26
27
|
alembic/operations/base.py,sha256=2so4KisDNuOLw0CRiZqorIHrhuenpVoFbn3B0sNvDic,72471
|
27
28
|
alembic/operations/batch.py,sha256=uMvGJDlcTs0GSHasg4Gsdv1YcXeLOK_1lkRl3jk1ezY,26954
|
28
|
-
alembic/operations/ops.py,sha256=
|
29
|
+
alembic/operations/ops.py,sha256=fkdAxh9PEOw7SluJDVEUx1sMuH8LqAnM-vCOk0pw4NU,93515
|
29
30
|
alembic/operations/schemaobj.py,sha256=-tWad8pgWUNWucbpTnPuFK_EEl913C0RADJhlBnrjhc,9393
|
30
|
-
alembic/operations/toimpl.py,sha256=
|
31
|
+
alembic/operations/toimpl.py,sha256=ZFdLsEITqOdJRuqq0DuiiLsexaN_AJou1F18rPpXLqI,6996
|
31
32
|
alembic/runtime/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
32
|
-
alembic/runtime/environment.py,sha256=
|
33
|
+
alembic/runtime/environment.py,sha256=kFTxvNhhmJRBXal-8AEnKr3hhL4rKLvJFfTGulp6uqY,41070
|
33
34
|
alembic/runtime/migration.py,sha256=5UtTI_T0JtYzt6ZpeUhannMZOvXWiEymKFOpeCefaPY,49407
|
34
35
|
alembic/script/__init__.py,sha256=lSj06O391Iy5avWAiq8SPs6N8RBgxkSPjP8wpXcNDGg,100
|
35
|
-
alembic/script/base.py,sha256=
|
36
|
+
alembic/script/base.py,sha256=qvlqkGZnAhN_npeciaPiMoo_QEhETnQqueHuUte3MnQ,37693
|
36
37
|
alembic/script/revision.py,sha256=DE0nwvDOzdFo843brvnhs1DfP0jRC5EVQHrNihC7PUQ,61471
|
37
38
|
alembic/script/write_hooks.py,sha256=Nqj4zz3sm97kAPOpK1m-i2znJchiybO_TWT50oljlJw,4917
|
38
39
|
alembic/templates/async/README,sha256=ISVtAOvqvKk_5ThM5ioJE-lMkvf9IbknFUFVU_vPma4,58
|
39
|
-
alembic/templates/async/alembic.ini.mako,sha256=
|
40
|
+
alembic/templates/async/alembic.ini.mako,sha256=uuhJETLWQuiYcs_jAOXHEjshEJ7VslEc1q4RRj0HWbE,3525
|
40
41
|
alembic/templates/async/env.py,sha256=zbOCf3Y7w2lg92hxSwmG1MM_7y56i_oRH4AKp0pQBYo,2389
|
41
42
|
alembic/templates/async/script.py.mako,sha256=MEqL-2qATlST9TAOeYgscMn1uy6HUS9NFvDgl93dMj8,635
|
42
43
|
alembic/templates/generic/README,sha256=MVlc9TYmr57RbhXET6QxgyCcwWP7w-vLkEsirENqiIQ,38
|
43
|
-
alembic/templates/generic/alembic.ini.mako,sha256=
|
44
|
+
alembic/templates/generic/alembic.ini.mako,sha256=sT7F852yN3c8X1-GKFlhuWExXxw9hY1eb1ZZ9flFSzc,3634
|
44
45
|
alembic/templates/generic/env.py,sha256=TLRWOVW3Xpt_Tpf8JFzlnoPn_qoUu8UV77Y4o9XD6yI,2103
|
45
46
|
alembic/templates/generic/script.py.mako,sha256=MEqL-2qATlST9TAOeYgscMn1uy6HUS9NFvDgl93dMj8,635
|
46
47
|
alembic/templates/multidb/README,sha256=dWLDhnBgphA4Nzb7sNlMfCS3_06YqVbHhz-9O5JNqyI,606
|
47
|
-
alembic/templates/multidb/alembic.ini.mako,sha256=
|
48
|
+
alembic/templates/multidb/alembic.ini.mako,sha256=mPh8JFJfWiGs6tMtL8_HAQ-Dz1QOoJgE5Vm76nIMqgU,3728
|
48
49
|
alembic/templates/multidb/env.py,sha256=6zNjnW8mXGUk7erTsAvrfhvqoczJ-gagjVq1Ypg2YIQ,4230
|
49
50
|
alembic/templates/multidb/script.py.mako,sha256=N06nMtNSwHkgl0EBXDyMt8njp9tlOesR583gfq21nbY,1090
|
50
51
|
alembic/testing/__init__.py,sha256=kOxOh5nwmui9d-_CCq9WA4Udwy7ITjm453w74CTLZDo,1159
|
51
52
|
alembic/testing/assertions.py,sha256=1CbJk8c8-WO9eJ0XJ0jJvMsNRLUrXV41NOeIJUAlOBk,5015
|
52
53
|
alembic/testing/env.py,sha256=zJacVb_z6uLs2U1TtkmnFH9P3_F-3IfYbVv4UEPOvfo,10754
|
53
54
|
alembic/testing/fixtures.py,sha256=NyP4wE_dFN9ZzSGiBagRu1cdzkka03nwJYJYHYrrkSY,9112
|
54
|
-
alembic/testing/requirements.py,sha256=
|
55
|
-
alembic/testing/schemacompare.py,sha256=
|
55
|
+
alembic/testing/requirements.py,sha256=dKeAO1l5TwBqXarJN-IPORlCqCJv-41Dj6oXoEikxHQ,5133
|
56
|
+
alembic/testing/schemacompare.py,sha256=N5UqSNCOJetIKC4vKhpYzQEpj08XkdgIoqBmEPQ3tlc,4838
|
56
57
|
alembic/testing/util.py,sha256=CQrcQDA8fs_7ME85z5ydb-Bt70soIIID-qNY1vbR2dg,3350
|
57
58
|
alembic/testing/warnings.py,sha256=RxA7x_8GseANgw07Us8JN_1iGbANxaw6_VitX2ZGQH4,1078
|
58
59
|
alembic/testing/plugin/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
@@ -67,16 +68,16 @@ alembic/testing/suite/test_autogen_identity.py,sha256=kcuqngG7qXAKPJDX4U8sRzPKHE
|
|
67
68
|
alembic/testing/suite/test_environment.py,sha256=w9F0xnLEbALeR8k6_-Tz6JHvy91IqiTSypNasVzXfZQ,11877
|
68
69
|
alembic/testing/suite/test_op.py,sha256=2XQCdm_NmnPxHGuGj7hmxMzIhKxXNotUsKdACXzE1mM,1343
|
69
70
|
alembic/util/__init__.py,sha256=cPF_jjFx7YRBByHHDqW3wxCIHsqnGfncEr_i238aduY,1202
|
70
|
-
alembic/util/compat.py,sha256=
|
71
|
+
alembic/util/compat.py,sha256=qKZ2A1o-iAfAZlr2QZGbFYC-bRyzwnIvXk9FmNXVEAU,2245
|
71
72
|
alembic/util/editor.py,sha256=JIz6_BdgV8_oKtnheR6DZoB7qnrHrlRgWjx09AsTsUw,2546
|
72
73
|
alembic/util/exc.py,sha256=KQTru4zcgAmN4IxLMwLFS56XToUewaXB7oOLcPNjPwg,98
|
73
74
|
alembic/util/langhelpers.py,sha256=ZFGyGygHRbztOeajpajppyhd-Gp4PB5slMuvCFVrnmg,8591
|
74
75
|
alembic/util/messaging.py,sha256=B6T-loMhIOY3OTbG47Ywp1Df9LZn18PgjwpwLrD1VNg,3042
|
75
76
|
alembic/util/pyfiles.py,sha256=95J01FChN0j2uP3p72mjaOQvh5wC6XbdGtTDK8oEzsQ,3373
|
76
|
-
alembic/util/sqla_compat.py,sha256=
|
77
|
-
alembic-1.
|
78
|
-
alembic-1.
|
79
|
-
alembic-1.
|
80
|
-
alembic-1.
|
81
|
-
alembic-1.
|
82
|
-
alembic-1.
|
77
|
+
alembic/util/sqla_compat.py,sha256=UDLxFKx2EGXjPg9JM_r3RkElRxJL50qGuHC868P_nbo,18806
|
78
|
+
alembic-1.13.0.dist-info/LICENSE,sha256=soUmiob0QW6vTQWyrjiAwVb3xZqPk1pAK8BW6vszrwg,1058
|
79
|
+
alembic-1.13.0.dist-info/METADATA,sha256=JsSQBNQNfhDiHpeIjCEu99_l6QC0MsB1tnGLqVhzUlk,7390
|
80
|
+
alembic-1.13.0.dist-info/WHEEL,sha256=oiQVh_5PnQM0E3gPdiz09WCNmwiHDMaGer_elqB3coM,92
|
81
|
+
alembic-1.13.0.dist-info/entry_points.txt,sha256=aykM30soxwGN0pB7etLc1q0cHJbL9dy46RnK9VX4LLw,48
|
82
|
+
alembic-1.13.0.dist-info/top_level.txt,sha256=FwKWd5VsPFC8iQjpu1u9Cn-JnK3-V1RhUCmWqz1cl-s,8
|
83
|
+
alembic-1.13.0.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|