Remove pre-SQLAlchemy-0.9.7 compat utilities
Now that requirements.txt is at SQLAlchemy 0.9.7 at the minimum, we can remove the engine_connect compatibility module as well as part of the handle_error compatibility module. In particular, this gives us the ability to add and remove engine_connect events using the event API directly which can be handy in test fixtures. Change-Id: I48450ad9d472d4377913ad391a0f5e3ba0f1471f
This commit is contained in:
parent
04eba07c67
commit
4d3a7a662e
@ -12,6 +12,5 @@
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from oslo_db.sqlalchemy.compat import engine_connect # noqa
|
||||
from oslo_db.sqlalchemy.compat import handle_error # noqa
|
||||
from oslo_db.sqlalchemy.compat import utils # noqa
|
||||
|
@ -16,13 +16,10 @@ added at some point but for which oslo.db provides a compatible versions
|
||||
for previous SQLAlchemy versions.
|
||||
|
||||
"""
|
||||
from oslo_db.sqlalchemy.compat import engine_connect as _e_conn
|
||||
from oslo_db.sqlalchemy.compat import handle_error as _h_err
|
||||
|
||||
# trying to get: "from oslo_db.sqlalchemy import compat; compat.handle_error"
|
||||
# flake8 won't let me import handle_error directly
|
||||
engine_connect = _e_conn.engine_connect
|
||||
handle_error = _h_err.handle_error
|
||||
|
||||
__all__ = [
|
||||
'engine_connect', 'handle_error']
|
||||
__all__ = ['handle_error']
|
||||
|
@ -1,60 +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.
|
||||
"""Provide forwards compatibility for the engine_connect event.
|
||||
|
||||
See the "engine_connect" event at
|
||||
http://docs.sqlalchemy.org/en/rel_0_9/core/events.html.
|
||||
|
||||
|
||||
"""
|
||||
|
||||
from sqlalchemy.engine import Engine
|
||||
from sqlalchemy import event
|
||||
|
||||
from oslo_db.sqlalchemy.compat import utils
|
||||
|
||||
|
||||
def engine_connect(engine, listener):
|
||||
"""Add an engine_connect listener for the given :class:`.Engine`.
|
||||
|
||||
This listener uses the SQLAlchemy
|
||||
:meth:`sqlalchemy.event.ConnectionEvents.engine_connect`
|
||||
event for 0.9.0 and above, and implements an interim listener
|
||||
for 0.8 versions.
|
||||
|
||||
"""
|
||||
if utils.sqla_090:
|
||||
event.listen(engine, "engine_connect", listener)
|
||||
return
|
||||
|
||||
assert isinstance(engine, Engine), \
|
||||
"engine argument must be an Engine instance, not a Connection"
|
||||
|
||||
if not getattr(engine._connection_cls,
|
||||
'_oslo_engine_connect_wrapper', False):
|
||||
engine._oslo_engine_connect_events = []
|
||||
|
||||
class Connection(engine._connection_cls):
|
||||
_oslo_engine_connect_wrapper = True
|
||||
|
||||
def __init__(self, *arg, **kw):
|
||||
super(Connection, self).__init__(*arg, **kw)
|
||||
|
||||
_oslo_engine_connect_events = getattr(
|
||||
self.engine,
|
||||
'_oslo_engine_connect_events',
|
||||
False)
|
||||
if _oslo_engine_connect_events:
|
||||
for fn in _oslo_engine_connect_events:
|
||||
fn(self, kw.get('_branch', False))
|
||||
engine._connection_cls = Connection
|
||||
engine._oslo_engine_connect_events.append(listener)
|
@ -32,8 +32,7 @@ def handle_error(engine, listener):
|
||||
|
||||
This listener uses the SQLAlchemy
|
||||
:meth:`sqlalchemy.event.ConnectionEvents.handle_error`
|
||||
event, however augments the listener for pre-0.9.7 versions of SQLAlchemy
|
||||
in order to support safe re-raise of the exception.
|
||||
event.
|
||||
|
||||
"""
|
||||
if utils.sqla_100:
|
||||
@ -43,122 +42,17 @@ def handle_error(engine, listener):
|
||||
assert isinstance(engine, Engine), \
|
||||
"engine argument must be an Engine instance, not a Connection"
|
||||
|
||||
if not utils.sqla_097:
|
||||
_rework_handle_exception_for_events(engine)
|
||||
engine._oslo_handle_error_events.append(listener)
|
||||
assert utils.sqla_097
|
||||
|
||||
_rework_connect_and_revalidate_for_events(engine)
|
||||
|
||||
if utils.sqla_097:
|
||||
# ctx.engine added per
|
||||
# https://bitbucket.org/zzzeek/sqlalchemy/issue/3266/
|
||||
def wrap_listener(ctx):
|
||||
if isinstance(ctx, engine_base.ExceptionContextImpl):
|
||||
ctx.engine = ctx.connection.engine
|
||||
return listener(ctx)
|
||||
event.listen(engine, "handle_error", wrap_listener)
|
||||
|
||||
|
||||
def _rework_handle_exception_for_events(engine):
|
||||
"""Patch the _handle_dbapi_error() system on Connection.
|
||||
|
||||
This allows the 0.9.7-style handle_error() event to be available on
|
||||
the Connection object.
|
||||
|
||||
"""
|
||||
engine._oslo_handle_error_events = []
|
||||
|
||||
class Connection(engine._connection_cls):
|
||||
def _handle_dbapi_exception(self, e, statement, parameters,
|
||||
cursor, context):
|
||||
|
||||
try:
|
||||
super(Connection, self)._handle_dbapi_exception(
|
||||
e, statement, parameters, cursor, context)
|
||||
except Exception as reraised_exception:
|
||||
# all versions:
|
||||
# _handle_dbapi_exception reraises all DBAPI errors
|
||||
# 0.8 and above:
|
||||
# reraises all errors unconditionally
|
||||
pass
|
||||
else:
|
||||
# 0.7.8:
|
||||
# _handle_dbapi_exception does not unconditionally
|
||||
# re-raise
|
||||
reraised_exception = e
|
||||
|
||||
_oslo_handle_error_events = getattr(
|
||||
self.engine,
|
||||
'_oslo_handle_error_events',
|
||||
False)
|
||||
|
||||
newraise = None
|
||||
if _oslo_handle_error_events:
|
||||
if isinstance(reraised_exception,
|
||||
sqla_exc.StatementError):
|
||||
sqlalchemy_exception = reraised_exception
|
||||
original_exception = sqlalchemy_exception.orig
|
||||
self._is_disconnect = is_disconnect = (
|
||||
isinstance(sqlalchemy_exception,
|
||||
sqla_exc.DBAPIError)
|
||||
and sqlalchemy_exception.connection_invalidated)
|
||||
else:
|
||||
sqlalchemy_exception = None
|
||||
original_exception = reraised_exception
|
||||
is_disconnect = False
|
||||
|
||||
# new handle_error event
|
||||
ctx = ExceptionContextImpl(
|
||||
original_exception, sqlalchemy_exception,
|
||||
self.engine, self, cursor, statement,
|
||||
parameters, context, is_disconnect)
|
||||
|
||||
for fn in _oslo_handle_error_events:
|
||||
try:
|
||||
# handler returns an exception;
|
||||
# call next handler in a chain
|
||||
per_fn = fn(ctx)
|
||||
if per_fn is not None:
|
||||
ctx.chained_exception = newraise = per_fn
|
||||
except Exception as _raised:
|
||||
# handler raises an exception - stop processing
|
||||
newraise = _raised
|
||||
break
|
||||
|
||||
if sqlalchemy_exception and \
|
||||
self._is_disconnect != ctx.is_disconnect:
|
||||
|
||||
if not ctx.is_disconnect:
|
||||
raise NotImplementedError(
|
||||
"Can't reset 'disconnect' status of exception "
|
||||
"once it is set with this version of "
|
||||
"SQLAlchemy")
|
||||
|
||||
sqlalchemy_exception.connection_invalidated = \
|
||||
self._is_disconnect = ctx.is_disconnect
|
||||
if self._is_disconnect:
|
||||
self._do_disconnect(e)
|
||||
|
||||
if newraise:
|
||||
six.reraise(type(newraise), newraise, sys.exc_info()[2])
|
||||
else:
|
||||
six.reraise(type(reraised_exception),
|
||||
reraised_exception, sys.exc_info()[2])
|
||||
|
||||
def _do_disconnect(self, e):
|
||||
del self._is_disconnect
|
||||
if utils.sqla_094:
|
||||
dbapi_conn_wrapper = self.connection
|
||||
self.engine.pool._invalidate(dbapi_conn_wrapper, e)
|
||||
self.invalidate(e)
|
||||
else:
|
||||
dbapi_conn_wrapper = self.connection
|
||||
self.invalidate(e)
|
||||
if not hasattr(dbapi_conn_wrapper, '_pool') or \
|
||||
dbapi_conn_wrapper._pool is self.engine.pool:
|
||||
self.engine.dispose()
|
||||
|
||||
engine._connection_cls = Connection
|
||||
# ctx.engine added per
|
||||
# https://bitbucket.org/zzzeek/sqlalchemy/issue/3266/
|
||||
def wrap_listener(ctx):
|
||||
if isinstance(ctx, engine_base.ExceptionContextImpl):
|
||||
ctx.engine = ctx.connection.engine
|
||||
return listener(ctx)
|
||||
event.listen(engine, "handle_error", wrap_listener)
|
||||
|
||||
|
||||
def _rework_connect_and_revalidate_for_events(engine):
|
||||
@ -335,6 +229,8 @@ class ExceptionContextImpl(object):
|
||||
This is for forwards compatibility with the
|
||||
ExceptionContext interface introduced in SQLAlchemy 0.9.7.
|
||||
|
||||
It also provides for the "engine" argument added in SQLAlchemy 1.0.0.
|
||||
|
||||
"""
|
||||
|
||||
def __init__(self, exception, sqlalchemy_exception,
|
||||
|
@ -286,6 +286,7 @@ import time
|
||||
|
||||
from oslo_utils import timeutils
|
||||
import six
|
||||
from sqlalchemy import event
|
||||
from sqlalchemy import exc
|
||||
import sqlalchemy.orm
|
||||
from sqlalchemy import pool
|
||||
@ -295,7 +296,6 @@ from sqlalchemy.sql.expression import select
|
||||
from oslo_db._i18n import _LW
|
||||
from oslo_db import exception
|
||||
from oslo_db import options
|
||||
from oslo_db.sqlalchemy import compat
|
||||
from oslo_db.sqlalchemy import exc_filters
|
||||
from oslo_db.sqlalchemy import update_match
|
||||
from oslo_db.sqlalchemy import utils
|
||||
@ -408,7 +408,7 @@ def create_engine(sql_connection, sqlite_fk=False, mysql_sql_mode=None,
|
||||
exc_filters.register_engine(engine)
|
||||
|
||||
# register engine connect handler
|
||||
compat.engine_connect(engine, _connect_ping_listener)
|
||||
event.listen(engine, "engine_connect", _connect_ping_listener)
|
||||
|
||||
# initial connect + test
|
||||
_test_connection(engine, max_retries, retry_interval)
|
||||
|
@ -1,68 +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.
|
||||
|
||||
"""Test the compatibility layer for the engine_connect() event.
|
||||
|
||||
This event is added as of SQLAlchemy 0.9.0; oslo.db provides a compatibility
|
||||
layer for prior SQLAlchemy versions.
|
||||
|
||||
"""
|
||||
|
||||
import mock
|
||||
from oslotest import base as test_base
|
||||
import sqlalchemy as sqla
|
||||
|
||||
from oslo.db.sqlalchemy import compat
|
||||
|
||||
|
||||
class EngineConnectTest(test_base.BaseTestCase):
|
||||
|
||||
def setUp(self):
|
||||
super(EngineConnectTest, self).setUp()
|
||||
|
||||
self.engine = engine = sqla.create_engine("sqlite://")
|
||||
self.addCleanup(engine.dispose)
|
||||
|
||||
def test_connect_event(self):
|
||||
engine = self.engine
|
||||
|
||||
listener = mock.Mock()
|
||||
compat.engine_connect(engine, listener)
|
||||
|
||||
conn = engine.connect()
|
||||
self.assertEqual(
|
||||
listener.mock_calls,
|
||||
[mock.call(conn, False)]
|
||||
)
|
||||
|
||||
conn.close()
|
||||
|
||||
conn2 = engine.connect()
|
||||
conn2.close()
|
||||
self.assertEqual(
|
||||
listener.mock_calls,
|
||||
[mock.call(conn, False), mock.call(conn2, False)]
|
||||
)
|
||||
|
||||
def test_branch(self):
|
||||
engine = self.engine
|
||||
|
||||
listener = mock.Mock()
|
||||
compat.engine_connect(engine, listener)
|
||||
|
||||
conn = engine.connect()
|
||||
branched = conn.connect()
|
||||
conn.close()
|
||||
self.assertEqual(
|
||||
listener.mock_calls,
|
||||
[mock.call(conn, False), mock.call(branched, True)]
|
||||
)
|
@ -19,10 +19,10 @@ import mock
|
||||
from oslotest import base as oslo_test_base
|
||||
import six
|
||||
import sqlalchemy as sqla
|
||||
from sqlalchemy import event
|
||||
from sqlalchemy.orm import mapper
|
||||
|
||||
from oslo.db import exception
|
||||
from oslo.db.sqlalchemy import compat
|
||||
from oslo.db.sqlalchemy import exc_filters
|
||||
from oslo.db.sqlalchemy import test_base
|
||||
from oslo_db.sqlalchemy import session as private_session
|
||||
@ -719,7 +719,8 @@ class TestDBDisconnected(TestsExceptionFilter):
|
||||
dialect_name, exception, num_disconnects, is_disconnect=True):
|
||||
engine = self.engine
|
||||
|
||||
compat.engine_connect(engine, private_session._connect_ping_listener)
|
||||
event.listen(
|
||||
engine, "engine_connect", private_session._connect_ping_listener)
|
||||
|
||||
real_do_execute = engine.dialect.do_execute
|
||||
counter = itertools.count(1)
|
||||
|
@ -1,68 +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.
|
||||
|
||||
"""Test the compatibility layer for the engine_connect() event.
|
||||
|
||||
This event is added as of SQLAlchemy 0.9.0; oslo_db provides a compatibility
|
||||
layer for prior SQLAlchemy versions.
|
||||
|
||||
"""
|
||||
|
||||
import mock
|
||||
from oslotest import base as test_base
|
||||
import sqlalchemy as sqla
|
||||
|
||||
from oslo_db.sqlalchemy.compat import engine_connect
|
||||
|
||||
|
||||
class EngineConnectTest(test_base.BaseTestCase):
|
||||
|
||||
def setUp(self):
|
||||
super(EngineConnectTest, self).setUp()
|
||||
|
||||
self.engine = engine = sqla.create_engine("sqlite://")
|
||||
self.addCleanup(engine.dispose)
|
||||
|
||||
def test_connect_event(self):
|
||||
engine = self.engine
|
||||
|
||||
listener = mock.Mock()
|
||||
engine_connect(engine, listener)
|
||||
|
||||
conn = engine.connect()
|
||||
self.assertEqual(
|
||||
listener.mock_calls,
|
||||
[mock.call(conn, False)]
|
||||
)
|
||||
|
||||
conn.close()
|
||||
|
||||
conn2 = engine.connect()
|
||||
conn2.close()
|
||||
self.assertEqual(
|
||||
listener.mock_calls,
|
||||
[mock.call(conn, False), mock.call(conn2, False)]
|
||||
)
|
||||
|
||||
def test_branch(self):
|
||||
engine = self.engine
|
||||
|
||||
listener = mock.Mock()
|
||||
engine_connect(engine, listener)
|
||||
|
||||
conn = engine.connect()
|
||||
branched = conn.connect()
|
||||
conn.close()
|
||||
self.assertEqual(
|
||||
listener.mock_calls,
|
||||
[mock.call(conn, False), mock.call(branched, True)]
|
||||
)
|
@ -19,10 +19,10 @@ import mock
|
||||
from oslotest import base as oslo_test_base
|
||||
import six
|
||||
import sqlalchemy as sqla
|
||||
from sqlalchemy import event
|
||||
from sqlalchemy.orm import mapper
|
||||
|
||||
from oslo_db import exception
|
||||
from oslo_db.sqlalchemy import compat
|
||||
from oslo_db.sqlalchemy import exc_filters
|
||||
from oslo_db.sqlalchemy import session
|
||||
from oslo_db.sqlalchemy import test_base
|
||||
@ -784,7 +784,7 @@ class TestDBDisconnected(TestsExceptionFilter):
|
||||
dialect_name, exception, num_disconnects, is_disconnect=True):
|
||||
engine = self.engine
|
||||
|
||||
compat.engine_connect(engine, session._connect_ping_listener)
|
||||
event.listen(engine, "engine_connect", session._connect_ping_listener)
|
||||
|
||||
real_do_execute = engine.dialect.do_execute
|
||||
counter = itertools.count(1)
|
||||
@ -966,7 +966,8 @@ class TestDBConnectPingWrapping(TestsExceptionFilter):
|
||||
|
||||
def setUp(self):
|
||||
super(TestDBConnectPingWrapping, self).setUp()
|
||||
compat.engine_connect(self.engine, session._connect_ping_listener)
|
||||
event.listen(
|
||||
self.engine, "engine_connect", session._connect_ping_listener)
|
||||
|
||||
@contextlib.contextmanager
|
||||
def _fixture(
|
||||
|
Loading…
Reference in New Issue
Block a user