Skip to content

Commit

Permalink
drop SQLAlchemy 1.3, Python 3.8
Browse files Browse the repository at this point in the history
Jenkins issues reveal that SQLAlchemy 1.3's testing provision has
unidentified deficiencies that are causing instability with the
mssql engine.   As provisioning was highly refactored in 1.4, just drop
1.3 support rather than trying to figure out the exact thing that
changed.

Support for SQLAlchemy 1.3, which was EOL as of 2021, is now dropped from
Alembic as of version 1.15.0.

Support for Python 3.8 is dropped as of Alembic 1.15.0.  Python 3.8 is EOL.

Try pytest 8.3 as this seems to be running fine for SQLAlchemy

Change-Id: I06614a6f73d5b1a1ac5b9b04225ecb305f5b35ee
  • Loading branch information
zzzeek committed Feb 9, 2025
1 parent d881db0 commit ab446ab
Show file tree
Hide file tree
Showing 37 changed files with 269 additions and 678 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -22,3 +22,4 @@ coverage.xml
/docs/build/_build/
/pysqlite_test_schema.db
*.sqlite3
.mypy_cache/
2 changes: 1 addition & 1 deletion alembic/__init__.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from . import context
from . import op

__version__ = "1.14.2"
__version__ = "1.15.0"
34 changes: 11 additions & 23 deletions alembic/autogenerate/compare.py
Original file line number Diff line number Diff line change
Expand Up @@ -216,7 +216,7 @@ def _compare_tables(
(inspector),
# fmt: on
)
sqla_compat._reflect_table(inspector, t)
inspector.reflect_table(t, include_columns=None)
if autogen_context.run_object_filters(t, tname, "table", True, None):
modify_table_ops = ops.ModifyTableOps(tname, [], schema=s)

Expand Down Expand Up @@ -246,7 +246,7 @@ def _compare_tables(
_compat_autogen_column_reflect(inspector),
# fmt: on
)
sqla_compat._reflect_table(inspector, t)
inspector.reflect_table(t, include_columns=None)
conn_column_info[(s, tname)] = t

for s, tname in sorted(existing_tables, key=lambda x: (x[0] or "", x[1])):
Expand Down Expand Up @@ -1067,27 +1067,15 @@ def _compare_server_default(
return False

if sqla_compat._server_default_is_computed(metadata_default):
# return False in case of a computed column as the server
# default. Note that DDL for adding or removing "GENERATED AS" from
# an existing column is not currently known for any backend.
# Once SQLAlchemy can reflect "GENERATED" as the "computed" element,
# we would also want to ignore and/or warn for changes vs. the
# metadata (or support backend specific DDL if applicable).
if not sqla_compat.has_computed_reflection:
return False

else:
return (
_compare_computed_default( # type:ignore[func-returns-value]
autogen_context,
alter_column_op,
schema,
tname,
cname,
conn_col,
metadata_col,
)
)
return _compare_computed_default( # type:ignore[func-returns-value]
autogen_context,
alter_column_op,
schema,
tname,
cname,
conn_col,
metadata_col,
)
if sqla_compat._server_default_is_computed(conn_col_default):
_warn_computed_not_supported(tname, cname)
return False
Expand Down
6 changes: 3 additions & 3 deletions alembic/autogenerate/render.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@
if TYPE_CHECKING:
from typing import Literal

from sqlalchemy import Computed
from sqlalchemy import Identity
from sqlalchemy.sql.base import DialectKWArgs
from sqlalchemy.sql.elements import ColumnElement
from sqlalchemy.sql.elements import TextClause
Expand All @@ -48,8 +50,6 @@
from alembic.config import Config
from alembic.operations.ops import MigrationScript
from alembic.operations.ops import ModifyTableOps
from alembic.util.sqla_compat import Computed
from alembic.util.sqla_compat import Identity


MAX_PYTHON_ARGS = 255
Expand Down Expand Up @@ -741,7 +741,7 @@ def _render_column(
+ [
"%s=%s"
% (key, _render_potential_expr(val, autogen_context))
for key, val in sqla_compat._column_kwargs(column).items()
for key, val in column.kwargs.items()
]
)
),
Expand Down
4 changes: 2 additions & 2 deletions alembic/ddl/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@
if TYPE_CHECKING:
from typing import Any

from sqlalchemy import Computed
from sqlalchemy import Identity
from sqlalchemy.sql.compiler import Compiled
from sqlalchemy.sql.compiler import DDLCompiler
from sqlalchemy.sql.elements import TextClause
Expand All @@ -33,8 +35,6 @@
from sqlalchemy.sql.type_api import TypeEngine

from .impl import DefaultImpl
from ..util.sqla_compat import Computed
from ..util.sqla_compat import Identity

_ServerDefault = Union["TextClause", "FetchedValue", "Function[Any]", str]

Expand Down
17 changes: 8 additions & 9 deletions alembic/ddl/impl.py
Original file line number Diff line number Diff line change
Expand Up @@ -459,7 +459,9 @@ def bulk_insert(
if self.as_sql:
for row in rows:
self._exec(
sqla_compat._insert_inline(table).values(
table.insert()
.inline()
.values(
**{
k: (
sqla_compat._literal_bindparam(
Expand All @@ -477,14 +479,10 @@ def bulk_insert(
else:
if rows:
if multiinsert:
self._exec(
sqla_compat._insert_inline(table), multiparams=rows
)
self._exec(table.insert().inline(), multiparams=rows)
else:
for row in rows:
self._exec(
sqla_compat._insert_inline(table).values(**row)
)
self._exec(table.insert().inline().values(**row))

def _tokenize_column_type(self, column: Column) -> Params:
definition: str
Expand Down Expand Up @@ -693,7 +691,7 @@ def _compare_identity_default(self, metadata_identity, inspector_identity):
diff, ignored = _compare_identity_options(
metadata_identity,
inspector_identity,
sqla_compat.Identity(),
schema.Identity(),
skip={"always"},
)

Expand Down Expand Up @@ -874,12 +872,13 @@ def check_dicts(
set(meta_d).union(insp_d),
)
if sqla_compat.identity_has_dialect_kwargs:
assert hasattr(default_io, "dialect_kwargs")
# use only the dialect kwargs in inspector_io since metadata_io
# can have options for many backends
check_dicts(
getattr(metadata_io, "dialect_kwargs", {}),
getattr(inspector_io, "dialect_kwargs", {}),
default_io.dialect_kwargs, # type: ignore[union-attr]
default_io.dialect_kwargs,
getattr(inspector_io, "dialect_kwargs", {}),
)

Expand Down
3 changes: 1 addition & 2 deletions alembic/ddl/mysql.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@
from .impl import DefaultImpl
from .. import util
from ..util import sqla_compat
from ..util.sqla_compat import _is_mariadb
from ..util.sqla_compat import _is_type_bound
from ..util.sqla_compat import compiles

Expand Down Expand Up @@ -475,7 +474,7 @@ def _mysql_drop_constraint(
# note that SQLAlchemy as of 1.2 does not yet support
# DROP CONSTRAINT for MySQL/MariaDB, so we implement fully
# here.
if _is_mariadb(compiler.dialect):
if compiler.dialect.is_mariadb: # type: ignore[attr-defined]
return "ALTER TABLE %s DROP CONSTRAINT %s" % (
compiler.preparer.format_table(constraint.table),
compiler.preparer.format_constraint(constraint),
Expand Down
8 changes: 4 additions & 4 deletions alembic/ddl/postgresql.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,10 @@

from sqlalchemy import Column
from sqlalchemy import Float
from sqlalchemy import Identity
from sqlalchemy import literal_column
from sqlalchemy import Numeric
from sqlalchemy import select
from sqlalchemy import text
from sqlalchemy import types as sqltypes
from sqlalchemy.dialects.postgresql import BIGINT
Expand Down Expand Up @@ -143,9 +145,7 @@ def compare_server_default(
conn = self.connection
assert conn is not None
return not conn.scalar(
sqla_compat._select(
literal_column(conn_col_default) == metadata_default
)
select(literal_column(conn_col_default) == metadata_default)
)

def alter_column( # type:ignore[override]
Expand Down Expand Up @@ -586,7 +586,7 @@ def visit_identity_column(
)
else:
text += "SET %s " % compiler.get_identity_options(
sqla_compat.Identity(**{attr: getattr(identity, attr)})
Identity(**{attr: getattr(identity, attr)})
)
return text

Expand Down
3 changes: 2 additions & 1 deletion alembic/ddl/sqlite.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
from typing import Union

from sqlalchemy import cast
from sqlalchemy import Computed
from sqlalchemy import JSON
from sqlalchemy import schema
from sqlalchemy import sql
Expand Down Expand Up @@ -64,7 +65,7 @@ def requires_recreate_in_batch(
) and isinstance(col.server_default.arg, sql.ClauseElement):
return True
elif (
isinstance(col.server_default, util.sqla_compat.Computed)
isinstance(col.server_default, Computed)
and col.server_default.persisted
):
return True
Expand Down
9 changes: 5 additions & 4 deletions alembic/operations/batch.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
from sqlalchemy import MetaData
from sqlalchemy import PrimaryKeyConstraint
from sqlalchemy import schema as sql_schema
from sqlalchemy import select
from sqlalchemy import Table
from sqlalchemy import types as sqltypes
from sqlalchemy.sql.schema import SchemaEventTarget
Expand All @@ -31,11 +32,9 @@
from ..util.sqla_compat import _ensure_scope_for_ddl
from ..util.sqla_compat import _fk_is_self_referential
from ..util.sqla_compat import _idx_table_bound_expressions
from ..util.sqla_compat import _insert_inline
from ..util.sqla_compat import _is_type_bound
from ..util.sqla_compat import _remove_column_from_collection
from ..util.sqla_compat import _resolve_for_variant
from ..util.sqla_compat import _select
from ..util.sqla_compat import constraint_name_defined
from ..util.sqla_compat import constraint_name_string

Expand Down Expand Up @@ -449,13 +448,15 @@ def _create(self, op_impl: DefaultImpl) -> None:

try:
op_impl._exec(
_insert_inline(self.new_table).from_select(
self.new_table.insert()
.inline()
.from_select(
list(
k
for k, transfer in self.column_transfers.items()
if "expr" in transfer
),
_select(
select(
*[
transfer["expr"]
for transfer in self.column_transfers.values()
Expand Down
13 changes: 0 additions & 13 deletions alembic/operations/toimpl.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@
from . import ops
from .base import Operations
from ..util.sqla_compat import _copy
from ..util.sqla_compat import sqla_14

if TYPE_CHECKING:
from sqlalchemy.sql.schema import Table
Expand Down Expand Up @@ -81,9 +80,6 @@ def _count_constraint(constraint):
def drop_table(operations: "Operations", operation: "ops.DropTableOp") -> None:
kw = {}
if operation.if_exists is not None:
if not sqla_14:
raise NotImplementedError("SQLAlchemy 1.4+ required")

kw["if_exists"] = operation.if_exists
operations.impl.drop_table(
operation.to_table(operations.migration_context), **kw
Expand All @@ -107,9 +103,6 @@ def create_index(
idx = operation.to_index(operations.migration_context)
kw = {}
if operation.if_not_exists is not None:
if not sqla_14:
raise NotImplementedError("SQLAlchemy 1.4+ required")

kw["if_not_exists"] = operation.if_not_exists
operations.impl.create_index(idx, **kw)

Expand All @@ -118,9 +111,6 @@ def create_index(
def drop_index(operations: "Operations", operation: "ops.DropIndexOp") -> None:
kw = {}
if operation.if_exists is not None:
if not sqla_14:
raise NotImplementedError("SQLAlchemy 1.4+ required")

kw["if_exists"] = operation.if_exists

operations.impl.drop_index(
Expand All @@ -135,9 +125,6 @@ def create_table(
) -> "Table":
kw = {}
if operation.if_not_exists is not None:
if not sqla_14:
raise NotImplementedError("SQLAlchemy 1.4+ required")

kw["if_not_exists"] = operation.if_not_exists
table = operation.to_table(operations.migration_context)
operations.impl.create_table(table, **kw)
Expand Down
4 changes: 2 additions & 2 deletions alembic/runtime/migration.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@

from sqlalchemy import Column
from sqlalchemy import literal_column
from sqlalchemy import select
from sqlalchemy.engine import Engine
from sqlalchemy.engine import url as sqla_url
from sqlalchemy.engine.strategies import MockEngineStrategy
Expand All @@ -32,7 +33,6 @@
from .. import util
from ..util import sqla_compat
from ..util.compat import EncodedIO
from ..util.sqla_compat import _select

if TYPE_CHECKING:
from sqlalchemy.engine import Dialect
Expand Down Expand Up @@ -534,7 +534,7 @@ def get_current_heads(self) -> Tuple[str, ...]:
return tuple(
row[0]
for row in self.connection.execute(
_select(self._version.c.version_num)
select(self._version.c.version_num)
)
)

Expand Down
5 changes: 2 additions & 3 deletions alembic/testing/assertions.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

from sqlalchemy import exc as sa_exc
from sqlalchemy.engine import default
from sqlalchemy.engine import URL
from sqlalchemy.testing.assertions import _expect_warnings
from sqlalchemy.testing.assertions import eq_ # noqa
from sqlalchemy.testing.assertions import is_ # noqa
Expand All @@ -17,8 +18,6 @@
from sqlalchemy.testing.assertions import ne_ # noqa
from sqlalchemy.util import decorator

from ..util import sqla_compat


def _assert_proper_exception_context(exception):
"""assert that any exception we're catching does not have a __context__
Expand Down Expand Up @@ -127,7 +126,7 @@ def _get_dialect(name):
if name is None or name == "default":
return default.DefaultDialect()
else:
d = sqla_compat._create_url(name).get_dialect()()
d = URL.create(name).get_dialect()()

if name == "postgresql":
d.implicit_returning = True
Expand Down
2 changes: 1 addition & 1 deletion alembic/testing/env.py
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ def env_file_fixture(txt):
def _sqlite_file_db(tempname="foo.db", future=False, scope=None, **options):
dir_ = _join_path(_get_staging_directory(), "scripts")
url = "sqlite:///%s/%s" % (dir_, tempname)
if scope and util.sqla_14:
if scope:
options["scope"] = scope
return testing_util.testing_engine(url=url, future=future, options=options)

Expand Down
Loading

0 comments on commit ab446ab

Please sign in to comment.