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 <stephenfin@redhat.com>
This commit is contained in:
Stephen Finucane 2019-12-18 10:56:13 +00:00
parent fafcbddec7
commit e4f2020b94
8 changed files with 17 additions and 129 deletions

View File

@ -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

View File

@ -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)

View File

@ -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()

View File

@ -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)

View File

@ -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(

View File

@ -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):

View File

@ -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