From e4f2020b94481c801ecd956dd695dd9130ced1d9 Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Wed, 18 Dec 2019 10:56:13 +0000 Subject: Raise minimum SQLAlchemy version to 1.2.0 Per [1], this is the latest supported version of SQLAlchemy. 1.1.x and earlier are EOL. [1] https://www.sqlalchemy.org/download.html#relstatus Change-Id: I63e4baf772be9ddfb787ac3aff01fcaddf7b901c Signed-off-by: Stephen Finucane --- lower-constraints.txt | 2 +- oslo_db/sqlalchemy/compat/__init__.py | 0 oslo_db/sqlalchemy/compat/utils.py | 69 ---------------------------- oslo_db/sqlalchemy/exc_filters.py | 7 +-- oslo_db/sqlalchemy/ndb.py | 5 +- oslo_db/tests/sqlalchemy/test_exc_filters.py | 44 ++++-------------- oslo_db/tests/sqlalchemy/test_utils.py | 17 +------ requirements.txt | 2 +- 8 files changed, 17 insertions(+), 129 deletions(-) delete mode 100644 oslo_db/sqlalchemy/compat/__init__.py delete mode 100644 oslo_db/sqlalchemy/compat/utils.py diff --git a/lower-constraints.txt b/lower-constraints.txt index 7c25647..34d6211 100644 --- a/lower-constraints.txt +++ b/lower-constraints.txt @@ -55,7 +55,7 @@ requestsexceptions==1.2.0 rfc3986==0.3.1 six==1.10.0 smmap==0.9.0 -SQLAlchemy==1.0.10 +SQLAlchemy==1.2.0 sqlalchemy-migrate==0.11.0 sqlparse==0.2.2 stevedore==1.20.0 diff --git a/oslo_db/sqlalchemy/compat/__init__.py b/oslo_db/sqlalchemy/compat/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/oslo_db/sqlalchemy/compat/utils.py b/oslo_db/sqlalchemy/compat/utils.py deleted file mode 100644 index 1d27a56..0000000 --- a/oslo_db/sqlalchemy/compat/utils.py +++ /dev/null @@ -1,69 +0,0 @@ -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. -import re - -import sqlalchemy - - -SQLA_VERSION = tuple( - int(num) if re.match(r'^\d+$', num) else num - for num in sqlalchemy.__version__.split(".") -) - -sqla_110 = SQLA_VERSION >= (1, 1, 0) -sqla_100 = SQLA_VERSION >= (1, 0, 0) -sqla_097 = SQLA_VERSION >= (0, 9, 7) -sqla_094 = SQLA_VERSION >= (0, 9, 4) -sqla_090 = SQLA_VERSION >= (0, 9, 0) -sqla_08 = SQLA_VERSION >= (0, 8) - - -def get_postgresql_enums(conn): - """Return a list of ENUM type names on a Postgresql backend. - - For SQLAlchemy 0.9 and lower, makes use of the semi-private - _load_enums() method of the Postgresql dialect. In SQLAlchemy - 1.0 this feature is supported using get_enums(). - - This function may only be called when the given connection - is against the Postgresql backend. It will fail for other - kinds of backends. - - """ - if sqla_100: - return [e['name'] for e in sqlalchemy.inspect(conn).get_enums()] - else: - return conn.dialect._load_enums(conn).keys() - - -def adapt_type_object(type_object, target_class, *args, **kw): - """Call the adapt() method on a type. - - For SQLAlchemy 1.0, runs a local version of constructor_copy() that - allows keyword arguments to be overridden. - - See https://github.com/zzzeek/sqlalchemy/commit/\ - ceeb033054f09db3eccbde3fad1941ec42919a54 - - """ - if sqla_110: - return type_object.adapt(target_class, *args, **kw) - else: - # NOTE(zzzeek): this only works for basic types, won't work for - # schema types like Enum, Boolean - # NOTE(zzzeek): this code can be removed once requirements - # are at SQLAlchemy >= 1.1 - names = sqlalchemy.util.get_cls_kwargs(target_class) - kw.update( - (k, type_object.__dict__[k]) for k in names.difference(kw) - if k in type_object.__dict__) - return target_class(*args, **kw) diff --git a/oslo_db/sqlalchemy/exc_filters.py b/oslo_db/sqlalchemy/exc_filters.py index 767c71c..bb9ba06 100644 --- a/oslo_db/sqlalchemy/exc_filters.py +++ b/oslo_db/sqlalchemy/exc_filters.py @@ -519,10 +519,5 @@ def register_engine(engine): def handle_connect_error(engine): - """Connect to the engine, including handle_error handlers. - - The compat library now builds this into the engine.connect() - system as per SQLAlchemy 1.0's behavior. - - """ + """Connect to the engine, including handle_error handlers.""" return engine.connect() diff --git a/oslo_db/sqlalchemy/ndb.py b/oslo_db/sqlalchemy/ndb.py index 59ec100..270b899 100644 --- a/oslo_db/sqlalchemy/ndb.py +++ b/oslo_db/sqlalchemy/ndb.py @@ -15,7 +15,6 @@ import re -from oslo_db.sqlalchemy.compat import utils as compat_utils from oslo_db.sqlalchemy.types import String from sqlalchemy import event, schema @@ -100,8 +99,8 @@ def _compile_ndb_string(element, compiler, **kw): return compiler.visit_string(element, **kw) if element.mysql_ndb_length: - effective_type = compat_utils.adapt_type_object( - element, _String, length=element.mysql_ndb_length) + effective_type = element.adapt( + _String, length=element.mysql_ndb_length) return compiler.visit_string(effective_type, **kw) elif element.mysql_ndb_type: effective_type = to_instance(element.mysql_ndb_type) diff --git a/oslo_db/tests/sqlalchemy/test_exc_filters.py b/oslo_db/tests/sqlalchemy/test_exc_filters.py index 2701632..6b3782a 100644 --- a/oslo_db/tests/sqlalchemy/test_exc_filters.py +++ b/oslo_db/tests/sqlalchemy/test_exc_filters.py @@ -28,7 +28,6 @@ from sqlalchemy.ext.declarative import declarative_base from sqlalchemy.orm import mapper from oslo_db import exception -from oslo_db.sqlalchemy.compat import utils as compat_utils from oslo_db.sqlalchemy import engines from oslo_db.sqlalchemy import exc_filters from oslo_db.tests.sqlalchemy import base as test_base @@ -655,48 +654,25 @@ class TestExceptionCauseMySQLSavepoint(test_base._MySQLOpportunisticTestCase): with session.begin(): session.add(self.A(id=1)) try: - with session.begin(): - try: with session.begin_nested(): session.execute("rollback") session.add(self.A(id=1)) - # outermost is the failed SAVEPOINT rollback # from the "with session.begin_nested()" except exception.DBError as dbe_inner: - - if not compat_utils.sqla_110: - # first "cause" is the failed SAVEPOINT rollback - # from inside of flush(), when it fails - self.assertTrue( - isinstance( - dbe_inner.cause, - exception.DBError - ) + # in SQLA 1.1+, the rollback() method of Session + # catches the error and repairs the state of the + # session even though the SAVEPOINT was lost; + # the net result here is that one exception is thrown + # instead of two. This is SQLAlchemy ticket #3680 + self.assertTrue( + isinstance( + dbe_inner.cause, + exception.DBDuplicateEntry ) - - # second "cause" is then the actual DB duplicate - self.assertTrue( - isinstance( - dbe_inner.cause.cause, - exception.DBDuplicateEntry - ) - ) - else: - # in SQLA 1.1, the rollback() method of Session - # catches the error and repairs the state of the - # session even though the SAVEPOINT was lost; - # the net result here is that one exception is thrown - # instead of two. This is SQLAlchemy ticket #3680 - self.assertTrue( - isinstance( - dbe_inner.cause, - exception.DBDuplicateEntry - ) - ) - + ) except exception.DBError as dbe_outer: self.assertTrue( isinstance( diff --git a/oslo_db/tests/sqlalchemy/test_utils.py b/oslo_db/tests/sqlalchemy/test_utils.py index c7526d5..ab468f7 100644 --- a/oslo_db/tests/sqlalchemy/test_utils.py +++ b/oslo_db/tests/sqlalchemy/test_utils.py @@ -23,6 +23,7 @@ from sqlalchemy import Boolean, Index, Integer, DateTime, String, SmallInteger from sqlalchemy import CheckConstraint from sqlalchemy import MetaData, Table, Column from sqlalchemy import ForeignKey, ForeignKeyConstraint +from sqlalchemy.dialects.postgresql import psycopg2 from sqlalchemy.engine import reflection from sqlalchemy.engine import url as sa_url from sqlalchemy.exc import OperationalError @@ -33,11 +34,9 @@ from sqlalchemy.orm import Session from sqlalchemy import PrimaryKeyConstraint from sqlalchemy.sql.expression import cast from sqlalchemy.sql import select -from sqlalchemy.types import UserDefinedType, NullType -from sqlalchemy.dialects.postgresql import psycopg2 +from sqlalchemy.types import UserDefinedType from oslo_db import exception -from oslo_db.sqlalchemy.compat import utils as compat_utils from oslo_db.sqlalchemy import models from oslo_db.sqlalchemy import provision from oslo_db.sqlalchemy import session @@ -47,7 +46,6 @@ from oslo_db.tests import utils as test_utils Base = declarative_base() -SA_VERSION = compat_utils.SQLA_VERSION class TestSanitizeDbUrl(test_base.BaseTestCase): @@ -841,12 +839,6 @@ class TestMigrationUtils(db_test_base._DbTestCase): Column('deleted', Boolean)) table.create() - # reflection of custom types has been fixed upstream - if SA_VERSION < (0, 9, 0): - self.assertRaises(exception.ColumnError, - utils.change_deleted_column_type_to_id_type, - self.engine, table_name) - fooColumn = Column('foo', CustomType()) utils.change_deleted_column_type_to_id_type(self.engine, table_name, foo=fooColumn) @@ -908,11 +900,6 @@ class TestMigrationUtils(db_test_base._DbTestCase): foo=fooColumn) table = utils.get_table(self.engine, table_name) - # NOTE(boris-42): There is no way to check has foo type CustomType. - # but sqlalchemy will set it to NullType. This has - # been fixed upstream in recent SA versions - if SA_VERSION < (0, 9, 0): - self.assertIsInstance(table.c.foo.type, NullType) self.assertIsInstance(table.c.deleted.type, Boolean) def test_detect_boolean_deleted_constraint_detection(self): diff --git a/requirements.txt b/requirements.txt index 4b30c00..c4cd79b 100644 --- a/requirements.txt +++ b/requirements.txt @@ -8,7 +8,7 @@ debtcollector>=1.2.0 # Apache-2.0 oslo.i18n>=3.15.3 # Apache-2.0 oslo.config>=5.2.0 # Apache-2.0 oslo.utils>=3.33.0 # Apache-2.0 -SQLAlchemy!=1.1.5,!=1.1.6,!=1.1.7,!=1.1.8,>=1.0.10 # MIT +SQLAlchemy>=1.2.0 # MIT sqlalchemy-migrate>=0.11.0 # Apache-2.0 stevedore>=1.20.0 # Apache-2.0 six>=1.10.0 # MIT -- cgit v1.2.1