Files
sqlalchemy/test/engine/test_deprecations.py
T
Mike Bayer 8b2ad56981 Enable F841
This is a very useful assertion which prevents unused variables
from being set up allows code to be more readable and sometimes
even more efficient.  test suites seem to be where the most
problems are and there do not seem to be documentation examples
that are using this, or at least the linter is not taking effect
within rst blocks.

Change-Id: I2b3341d8dd14da34879d8425838e66a4b9f8e27d
(cherry picked from commit 190e0139e8)
2019-06-21 10:18:17 -04:00

1794 lines
57 KiB
Python

import re
import time
import sqlalchemy as tsa
from sqlalchemy import column
from sqlalchemy import create_engine
from sqlalchemy import engine_from_config
from sqlalchemy import event
from sqlalchemy import ForeignKey
from sqlalchemy import func
from sqlalchemy import inspect
from sqlalchemy import INT
from sqlalchemy import Integer
from sqlalchemy import literal
from sqlalchemy import MetaData
from sqlalchemy import pool
from sqlalchemy import select
from sqlalchemy import Sequence
from sqlalchemy import String
from sqlalchemy import testing
from sqlalchemy import text
from sqlalchemy import TypeDecorator
from sqlalchemy import VARCHAR
from sqlalchemy.engine.base import Engine
from sqlalchemy.interfaces import ConnectionProxy
from sqlalchemy.testing import assert_raises_message
from sqlalchemy.testing import engines
from sqlalchemy.testing import eq_
from sqlalchemy.testing import fixtures
from sqlalchemy.testing.engines import testing_engine
from sqlalchemy.testing.mock import call
from sqlalchemy.testing.mock import Mock
from sqlalchemy.testing.schema import Column
from sqlalchemy.testing.schema import Table
from sqlalchemy.testing.util import gc_collect
from sqlalchemy.testing.util import lazy_gc
from .test_parseconnect import mock_dbapi
tlengine = None
class SomeException(Exception):
pass
def _tlengine_deprecated():
return testing.expect_deprecated(
"The 'threadlocal' engine strategy is deprecated"
)
class TableNamesOrderByTest(fixtures.TestBase):
@testing.provide_metadata
def test_order_by_foreign_key(self):
Table(
"t1",
self.metadata,
Column("id", Integer, primary_key=True),
test_needs_acid=True,
)
Table(
"t2",
self.metadata,
Column("id", Integer, primary_key=True),
Column("t1id", Integer, ForeignKey("t1.id")),
test_needs_acid=True,
)
Table(
"t3",
self.metadata,
Column("id", Integer, primary_key=True),
Column("t2id", Integer, ForeignKey("t2.id")),
test_needs_acid=True,
)
self.metadata.create_all()
insp = inspect(testing.db)
with testing.expect_deprecated(
"The get_table_names.order_by parameter is deprecated "
):
tnames = insp.get_table_names(order_by="foreign_key")
eq_(tnames, ["t1", "t2", "t3"])
class CreateEngineTest(fixtures.TestBase):
def test_pool_threadlocal_from_config(self):
dbapi = mock_dbapi
config = {
"sqlalchemy.url": "postgresql://scott:tiger@somehost/test",
"sqlalchemy.pool_threadlocal": "false",
}
e = engine_from_config(config, module=dbapi, _initialize=False)
eq_(e.pool._use_threadlocal, False)
config = {
"sqlalchemy.url": "postgresql://scott:tiger@somehost/test",
"sqlalchemy.pool_threadlocal": "true",
}
with testing.expect_deprecated(
"The Pool.use_threadlocal parameter is deprecated"
):
e = engine_from_config(config, module=dbapi, _initialize=False)
eq_(e.pool._use_threadlocal, True)
class RecycleTest(fixtures.TestBase):
__backend__ = True
def test_basic(self):
with testing.expect_deprecated(
"The Pool.use_threadlocal parameter is deprecated"
):
engine = engines.reconnecting_engine(
options={"pool_threadlocal": True}
)
with testing.expect_deprecated(
r"The Engine.contextual_connect\(\) method is deprecated"
):
conn = engine.contextual_connect()
eq_(conn.execute(select([1])).scalar(), 1)
conn.close()
# set the pool recycle down to 1.
# we aren't doing this inline with the
# engine create since cx_oracle takes way
# too long to create the 1st connection and don't
# want to build a huge delay into this test.
engine.pool._recycle = 1
# kill the DB connection
engine.test_shutdown()
# wait until past the recycle period
time.sleep(2)
# can connect, no exception
with testing.expect_deprecated(
r"The Engine.contextual_connect\(\) method is deprecated"
):
conn = engine.contextual_connect()
eq_(conn.execute(select([1])).scalar(), 1)
conn.close()
class TLTransactionTest(fixtures.TestBase):
__requires__ = ("ad_hoc_engines",)
__backend__ = True
@classmethod
def setup_class(cls):
global users, metadata, tlengine
with _tlengine_deprecated():
tlengine = testing_engine(options=dict(strategy="threadlocal"))
metadata = MetaData()
users = Table(
"query_users",
metadata,
Column(
"user_id",
INT,
Sequence("query_users_id_seq", optional=True),
primary_key=True,
),
Column("user_name", VARCHAR(20)),
test_needs_acid=True,
)
metadata.create_all(tlengine)
def teardown(self):
tlengine.execute(users.delete()).close()
@classmethod
def teardown_class(cls):
tlengine.close()
metadata.drop_all(tlengine)
tlengine.dispose()
def setup(self):
# ensure tests start with engine closed
tlengine.close()
@testing.crashes(
"oracle", "TNS error of unknown origin occurs on the buildbot."
)
def test_rollback_no_trans(self):
with _tlengine_deprecated():
tlengine = testing_engine(options=dict(strategy="threadlocal"))
# shouldn't fail
tlengine.rollback()
tlengine.begin()
tlengine.rollback()
# shouldn't fail
tlengine.rollback()
def test_commit_no_trans(self):
with _tlengine_deprecated():
tlengine = testing_engine(options=dict(strategy="threadlocal"))
# shouldn't fail
tlengine.commit()
tlengine.begin()
tlengine.rollback()
# shouldn't fail
tlengine.commit()
def test_prepare_no_trans(self):
with _tlengine_deprecated():
tlengine = testing_engine(options=dict(strategy="threadlocal"))
# shouldn't fail
tlengine.prepare()
tlengine.begin()
tlengine.rollback()
# shouldn't fail
tlengine.prepare()
def test_connection_close(self):
"""test that when connections are closed for real, transactions
are rolled back and disposed."""
c = tlengine.contextual_connect()
c.begin()
assert c.in_transaction()
c.close()
assert not c.in_transaction()
def test_transaction_close(self):
c = tlengine.contextual_connect()
t = c.begin()
tlengine.execute(users.insert(), user_id=1, user_name="user1")
tlengine.execute(users.insert(), user_id=2, user_name="user2")
t2 = c.begin()
tlengine.execute(users.insert(), user_id=3, user_name="user3")
tlengine.execute(users.insert(), user_id=4, user_name="user4")
t2.close()
result = c.execute("select * from query_users")
assert len(result.fetchall()) == 4
t.close()
external_connection = tlengine.connect()
result = external_connection.execute("select * from query_users")
try:
assert len(result.fetchall()) == 0
finally:
c.close()
external_connection.close()
def test_rollback(self):
"""test a basic rollback"""
tlengine.begin()
tlengine.execute(users.insert(), user_id=1, user_name="user1")
tlengine.execute(users.insert(), user_id=2, user_name="user2")
tlengine.execute(users.insert(), user_id=3, user_name="user3")
tlengine.rollback()
external_connection = tlengine.connect()
result = external_connection.execute("select * from query_users")
try:
assert len(result.fetchall()) == 0
finally:
external_connection.close()
def test_commit(self):
"""test a basic commit"""
tlengine.begin()
tlengine.execute(users.insert(), user_id=1, user_name="user1")
tlengine.execute(users.insert(), user_id=2, user_name="user2")
tlengine.execute(users.insert(), user_id=3, user_name="user3")
tlengine.commit()
external_connection = tlengine.connect()
result = external_connection.execute("select * from query_users")
try:
assert len(result.fetchall()) == 3
finally:
external_connection.close()
def test_with_interface(self):
trans = tlengine.begin()
tlengine.execute(users.insert(), user_id=1, user_name="user1")
tlengine.execute(users.insert(), user_id=2, user_name="user2")
trans.commit()
trans = tlengine.begin()
tlengine.execute(users.insert(), user_id=3, user_name="user3")
trans.__exit__(Exception, "fake", None)
trans = tlengine.begin()
tlengine.execute(users.insert(), user_id=4, user_name="user4")
trans.__exit__(None, None, None)
eq_(
tlengine.execute(
users.select().order_by(users.c.user_id)
).fetchall(),
[(1, "user1"), (2, "user2"), (4, "user4")],
)
def test_commits(self):
connection = tlengine.connect()
assert (
connection.execute("select count(*) from query_users").scalar()
== 0
)
connection.close()
connection = tlengine.contextual_connect()
transaction = connection.begin()
connection.execute(users.insert(), user_id=1, user_name="user1")
transaction.commit()
transaction = connection.begin()
connection.execute(users.insert(), user_id=2, user_name="user2")
connection.execute(users.insert(), user_id=3, user_name="user3")
transaction.commit()
transaction = connection.begin()
result = connection.execute("select * from query_users")
rows = result.fetchall()
assert len(rows) == 3, "expected 3 got %d" % len(rows)
transaction.commit()
connection.close()
def test_rollback_off_conn(self):
# test that a TLTransaction opened off a TLConnection allows
# that TLConnection to be aware of the transactional context
conn = tlengine.contextual_connect()
trans = conn.begin()
conn.execute(users.insert(), user_id=1, user_name="user1")
conn.execute(users.insert(), user_id=2, user_name="user2")
conn.execute(users.insert(), user_id=3, user_name="user3")
trans.rollback()
external_connection = tlengine.connect()
result = external_connection.execute("select * from query_users")
try:
assert len(result.fetchall()) == 0
finally:
conn.close()
external_connection.close()
def test_morerollback_off_conn(self):
# test that an existing TLConnection automatically takes place
# in a TLTransaction opened on a second TLConnection
conn = tlengine.contextual_connect()
conn2 = tlengine.contextual_connect()
trans = conn2.begin()
conn.execute(users.insert(), user_id=1, user_name="user1")
conn.execute(users.insert(), user_id=2, user_name="user2")
conn.execute(users.insert(), user_id=3, user_name="user3")
trans.rollback()
external_connection = tlengine.connect()
result = external_connection.execute("select * from query_users")
try:
assert len(result.fetchall()) == 0
finally:
conn.close()
conn2.close()
external_connection.close()
def test_commit_off_connection(self):
conn = tlengine.contextual_connect()
trans = conn.begin()
conn.execute(users.insert(), user_id=1, user_name="user1")
conn.execute(users.insert(), user_id=2, user_name="user2")
conn.execute(users.insert(), user_id=3, user_name="user3")
trans.commit()
external_connection = tlengine.connect()
result = external_connection.execute("select * from query_users")
try:
assert len(result.fetchall()) == 3
finally:
conn.close()
external_connection.close()
def test_nesting_rollback(self):
"""tests nesting of transactions, rollback at the end"""
external_connection = tlengine.connect()
self.assert_(
external_connection.connection
is not tlengine.contextual_connect().connection
)
tlengine.begin()
tlengine.execute(users.insert(), user_id=1, user_name="user1")
tlengine.execute(users.insert(), user_id=2, user_name="user2")
tlengine.execute(users.insert(), user_id=3, user_name="user3")
tlengine.begin()
tlengine.execute(users.insert(), user_id=4, user_name="user4")
tlengine.execute(users.insert(), user_id=5, user_name="user5")
tlengine.commit()
tlengine.rollback()
try:
self.assert_(
external_connection.scalar("select count(*) from query_users")
== 0
)
finally:
external_connection.close()
def test_nesting_commit(self):
"""tests nesting of transactions, commit at the end."""
external_connection = tlengine.connect()
self.assert_(
external_connection.connection
is not tlengine.contextual_connect().connection
)
tlengine.begin()
tlengine.execute(users.insert(), user_id=1, user_name="user1")
tlengine.execute(users.insert(), user_id=2, user_name="user2")
tlengine.execute(users.insert(), user_id=3, user_name="user3")
tlengine.begin()
tlengine.execute(users.insert(), user_id=4, user_name="user4")
tlengine.execute(users.insert(), user_id=5, user_name="user5")
tlengine.commit()
tlengine.commit()
try:
self.assert_(
external_connection.scalar("select count(*) from query_users")
== 5
)
finally:
external_connection.close()
def test_mixed_nesting(self):
"""tests nesting of transactions off the TLEngine directly
inside of transactions off the connection from the TLEngine"""
external_connection = tlengine.connect()
self.assert_(
external_connection.connection
is not tlengine.contextual_connect().connection
)
conn = tlengine.contextual_connect()
trans = conn.begin()
trans2 = conn.begin()
tlengine.execute(users.insert(), user_id=1, user_name="user1")
tlengine.execute(users.insert(), user_id=2, user_name="user2")
tlengine.execute(users.insert(), user_id=3, user_name="user3")
tlengine.begin()
tlengine.execute(users.insert(), user_id=4, user_name="user4")
tlengine.begin()
tlengine.execute(users.insert(), user_id=5, user_name="user5")
tlengine.execute(users.insert(), user_id=6, user_name="user6")
tlengine.execute(users.insert(), user_id=7, user_name="user7")
tlengine.commit()
tlengine.execute(users.insert(), user_id=8, user_name="user8")
tlengine.commit()
trans2.commit()
trans.rollback()
conn.close()
try:
self.assert_(
external_connection.scalar("select count(*) from query_users")
== 0
)
finally:
external_connection.close()
def test_more_mixed_nesting(self):
"""tests nesting of transactions off the connection from the
TLEngine inside of transactions off the TLEngine directly."""
external_connection = tlengine.connect()
self.assert_(
external_connection.connection
is not tlengine.contextual_connect().connection
)
tlengine.begin()
connection = tlengine.contextual_connect()
connection.execute(users.insert(), user_id=1, user_name="user1")
tlengine.begin()
connection.execute(users.insert(), user_id=2, user_name="user2")
connection.execute(users.insert(), user_id=3, user_name="user3")
trans = connection.begin()
connection.execute(users.insert(), user_id=4, user_name="user4")
connection.execute(users.insert(), user_id=5, user_name="user5")
trans.commit()
tlengine.commit()
tlengine.rollback()
connection.close()
try:
self.assert_(
external_connection.scalar("select count(*) from query_users")
== 0
)
finally:
external_connection.close()
@testing.requires.savepoints
def test_nested_subtransaction_rollback(self):
tlengine.begin()
tlengine.execute(users.insert(), user_id=1, user_name="user1")
tlengine.begin_nested()
tlengine.execute(users.insert(), user_id=2, user_name="user2")
tlengine.rollback()
tlengine.execute(users.insert(), user_id=3, user_name="user3")
tlengine.commit()
tlengine.close()
eq_(
tlengine.execute(
select([users.c.user_id]).order_by(users.c.user_id)
).fetchall(),
[(1,), (3,)],
)
tlengine.close()
@testing.requires.savepoints
@testing.crashes(
"oracle+zxjdbc",
"Errors out and causes subsequent tests to " "deadlock",
)
def test_nested_subtransaction_commit(self):
tlengine.begin()
tlengine.execute(users.insert(), user_id=1, user_name="user1")
tlengine.begin_nested()
tlengine.execute(users.insert(), user_id=2, user_name="user2")
tlengine.commit()
tlengine.execute(users.insert(), user_id=3, user_name="user3")
tlengine.commit()
tlengine.close()
eq_(
tlengine.execute(
select([users.c.user_id]).order_by(users.c.user_id)
).fetchall(),
[(1,), (2,), (3,)],
)
tlengine.close()
@testing.requires.savepoints
def test_rollback_to_subtransaction(self):
tlengine.begin()
tlengine.execute(users.insert(), user_id=1, user_name="user1")
tlengine.begin_nested()
tlengine.execute(users.insert(), user_id=2, user_name="user2")
tlengine.begin()
tlengine.execute(users.insert(), user_id=3, user_name="user3")
tlengine.rollback()
tlengine.rollback()
tlengine.execute(users.insert(), user_id=4, user_name="user4")
tlengine.commit()
tlengine.close()
eq_(
tlengine.execute(
select([users.c.user_id]).order_by(users.c.user_id)
).fetchall(),
[(1,), (4,)],
)
tlengine.close()
def test_connections(self):
"""tests that contextual_connect is threadlocal"""
c1 = tlengine.contextual_connect()
c2 = tlengine.contextual_connect()
assert c1.connection is c2.connection
c2.close()
assert not c1.closed
assert not tlengine.closed
@testing.requires.independent_cursors
def test_result_closing(self):
"""tests that contextual_connect is threadlocal"""
r1 = tlengine.execute(select([1]))
r2 = tlengine.execute(select([1]))
r1.fetchone()
r2.fetchone()
r1.close()
assert r2.connection is r1.connection
assert not r2.connection.closed
assert not tlengine.closed
# close again, nothing happens since resultproxy calls close()
# only once
r1.close()
assert r2.connection is r1.connection
assert not r2.connection.closed
assert not tlengine.closed
r2.close()
assert r2.connection.closed
assert tlengine.closed
@testing.crashes(
"oracle+cx_oracle", "intermittent failures on the buildbot"
)
def test_dispose(self):
with _tlengine_deprecated():
eng = testing_engine(options=dict(strategy="threadlocal"))
eng.execute(select([1]))
eng.dispose()
eng.execute(select([1]))
@testing.requires.two_phase_transactions
def test_two_phase_transaction(self):
tlengine.begin_twophase()
tlengine.execute(users.insert(), user_id=1, user_name="user1")
tlengine.prepare()
tlengine.commit()
tlengine.begin_twophase()
tlengine.execute(users.insert(), user_id=2, user_name="user2")
tlengine.commit()
tlengine.begin_twophase()
tlengine.execute(users.insert(), user_id=3, user_name="user3")
tlengine.rollback()
tlengine.begin_twophase()
tlengine.execute(users.insert(), user_id=4, user_name="user4")
tlengine.prepare()
tlengine.rollback()
eq_(
tlengine.execute(
select([users.c.user_id]).order_by(users.c.user_id)
).fetchall(),
[(1,), (2,)],
)
class ConvenienceExecuteTest(fixtures.TablesTest):
__backend__ = True
@classmethod
def define_tables(cls, metadata):
cls.table = Table(
"exec_test",
metadata,
Column("a", Integer),
Column("b", Integer),
test_needs_acid=True,
)
def _trans_fn(self, is_transaction=False):
def go(conn, x, value=None):
if is_transaction:
conn = conn.connection
conn.execute(self.table.insert().values(a=x, b=value))
return go
def _trans_rollback_fn(self, is_transaction=False):
def go(conn, x, value=None):
if is_transaction:
conn = conn.connection
conn.execute(self.table.insert().values(a=x, b=value))
raise SomeException("breakage")
return go
def _assert_no_data(self):
eq_(
testing.db.scalar(
select([func.count("*")]).select_from(self.table)
),
0,
)
def _assert_fn(self, x, value=None):
eq_(testing.db.execute(self.table.select()).fetchall(), [(x, value)])
def test_transaction_tlocal_engine_ctx_commit(self):
fn = self._trans_fn()
with _tlengine_deprecated():
engine = engines.testing_engine(
options=dict(strategy="threadlocal", pool=testing.db.pool)
)
ctx = engine.begin()
testing.run_as_contextmanager(ctx, fn, 5, value=8)
self._assert_fn(5, value=8)
def test_transaction_tlocal_engine_ctx_rollback(self):
fn = self._trans_rollback_fn()
with _tlengine_deprecated():
engine = engines.testing_engine(
options=dict(strategy="threadlocal", pool=testing.db.pool)
)
ctx = engine.begin()
assert_raises_message(
Exception,
"breakage",
testing.run_as_contextmanager,
ctx,
fn,
5,
value=8,
)
self._assert_no_data()
def _proxy_execute_deprecated():
return (
testing.expect_deprecated("ConnectionProxy.execute is deprecated."),
testing.expect_deprecated(
"ConnectionProxy.cursor_execute is deprecated."
),
)
class ProxyConnectionTest(fixtures.TestBase):
"""These are the same tests as EngineEventsTest, except using
the deprecated ConnectionProxy interface.
"""
__requires__ = ("ad_hoc_engines",)
__prefer_requires__ = ("two_phase_transactions",)
@testing.uses_deprecated(r".*Use event.listen")
@testing.fails_on("firebird", "Data type unknown")
def test_proxy(self):
stmts = []
cursor_stmts = []
class MyProxy(ConnectionProxy):
def execute(
self, conn, execute, clauseelement, *multiparams, **params
):
stmts.append((str(clauseelement), params, multiparams))
return execute(clauseelement, *multiparams, **params)
def cursor_execute(
self,
execute,
cursor,
statement,
parameters,
context,
executemany,
):
cursor_stmts.append((str(statement), parameters, None))
return execute(cursor, statement, parameters, context)
def assert_stmts(expected, received):
for stmt, params, posn in expected:
if not received:
assert False, "Nothing available for stmt: %s" % stmt
while received:
teststmt, testparams, testmultiparams = received.pop(0)
teststmt = (
re.compile(r"[\n\t ]+", re.M)
.sub(" ", teststmt)
.strip()
)
if teststmt.startswith(stmt) and (
testparams == params or testparams == posn
):
break
with testing.expect_deprecated(
"ConnectionProxy.execute is deprecated.",
"ConnectionProxy.cursor_execute is deprecated.",
):
plain_engine = engines.testing_engine(
options=dict(implicit_returning=False, proxy=MyProxy())
)
with testing.expect_deprecated(
"ConnectionProxy.execute is deprecated.",
"ConnectionProxy.cursor_execute is deprecated.",
"The 'threadlocal' engine strategy is deprecated",
):
tl_engine = engines.testing_engine(
options=dict(
implicit_returning=False,
proxy=MyProxy(),
strategy="threadlocal",
)
)
for engine in (plain_engine, tl_engine):
m = MetaData(engine)
t1 = Table(
"t1",
m,
Column("c1", Integer, primary_key=True),
Column(
"c2",
String(50),
default=func.lower("Foo"),
primary_key=True,
),
)
m.create_all()
try:
t1.insert().execute(c1=5, c2="some data")
t1.insert().execute(c1=6)
eq_(
engine.execute("select * from t1").fetchall(),
[(5, "some data"), (6, "foo")],
)
finally:
m.drop_all()
engine.dispose()
compiled = [
("CREATE TABLE t1", {}, None),
(
"INSERT INTO t1 (c1, c2)",
{"c2": "some data", "c1": 5},
None,
),
("INSERT INTO t1 (c1, c2)", {"c1": 6}, None),
("select * from t1", {}, None),
("DROP TABLE t1", {}, None),
]
cursor = [
("CREATE TABLE t1", {}, ()),
(
"INSERT INTO t1 (c1, c2)",
{"c2": "some data", "c1": 5},
(5, "some data"),
),
("SELECT lower", {"lower_1": "Foo"}, ("Foo",)),
(
"INSERT INTO t1 (c1, c2)",
{"c2": "foo", "c1": 6},
(6, "foo"),
),
("select * from t1", {}, ()),
("DROP TABLE t1", {}, ()),
]
assert_stmts(compiled, stmts)
assert_stmts(cursor, cursor_stmts)
@testing.uses_deprecated(r".*Use event.listen")
def test_options(self):
canary = []
class TrackProxy(ConnectionProxy):
def __getattribute__(self, key):
fn = object.__getattribute__(self, key)
def go(*arg, **kw):
canary.append(fn.__name__)
return fn(*arg, **kw)
return go
with testing.expect_deprecated(
*[
"ConnectionProxy.%s is deprecated" % name
for name in [
"execute",
"cursor_execute",
"begin",
"rollback",
"commit",
"savepoint",
"rollback_savepoint",
"release_savepoint",
"begin_twophase",
"prepare_twophase",
"rollback_twophase",
"commit_twophase",
]
]
):
engine = engines.testing_engine(options={"proxy": TrackProxy()})
conn = engine.connect()
c2 = conn.execution_options(foo="bar")
eq_(c2._execution_options, {"foo": "bar"})
c2.execute(select([1]))
c3 = c2.execution_options(bar="bat")
eq_(c3._execution_options, {"foo": "bar", "bar": "bat"})
eq_(canary, ["execute", "cursor_execute"])
@testing.uses_deprecated(r".*Use event.listen")
def test_transactional(self):
canary = []
class TrackProxy(ConnectionProxy):
def __getattribute__(self, key):
fn = object.__getattribute__(self, key)
def go(*arg, **kw):
canary.append(fn.__name__)
return fn(*arg, **kw)
return go
with testing.expect_deprecated(
*[
"ConnectionProxy.%s is deprecated" % name
for name in [
"execute",
"cursor_execute",
"begin",
"rollback",
"commit",
"savepoint",
"rollback_savepoint",
"release_savepoint",
"begin_twophase",
"prepare_twophase",
"rollback_twophase",
"commit_twophase",
]
]
):
engine = engines.testing_engine(options={"proxy": TrackProxy()})
conn = engine.connect()
trans = conn.begin()
conn.execute(select([1]))
trans.rollback()
trans = conn.begin()
conn.execute(select([1]))
trans.commit()
eq_(
canary,
[
"begin",
"execute",
"cursor_execute",
"rollback",
"begin",
"execute",
"cursor_execute",
"commit",
],
)
@testing.uses_deprecated(r".*Use event.listen")
@testing.requires.savepoints
@testing.requires.two_phase_transactions
def test_transactional_advanced(self):
canary = []
class TrackProxy(ConnectionProxy):
def __getattribute__(self, key):
fn = object.__getattribute__(self, key)
def go(*arg, **kw):
canary.append(fn.__name__)
return fn(*arg, **kw)
return go
with testing.expect_deprecated(
*[
"ConnectionProxy.%s is deprecated" % name
for name in [
"execute",
"cursor_execute",
"begin",
"rollback",
"commit",
"savepoint",
"rollback_savepoint",
"release_savepoint",
"begin_twophase",
"prepare_twophase",
"rollback_twophase",
"commit_twophase",
]
]
):
engine = engines.testing_engine(options={"proxy": TrackProxy()})
conn = engine.connect()
trans = conn.begin()
trans2 = conn.begin_nested()
conn.execute(select([1]))
trans2.rollback()
trans2 = conn.begin_nested()
conn.execute(select([1]))
trans2.commit()
trans.rollback()
trans = conn.begin_twophase()
conn.execute(select([1]))
trans.prepare()
trans.commit()
canary = [t for t in canary if t not in ("cursor_execute", "execute")]
eq_(
canary,
[
"begin",
"savepoint",
"rollback_savepoint",
"savepoint",
"release_savepoint",
"rollback",
"begin_twophase",
"prepare_twophase",
"commit_twophase",
],
)
class HandleInvalidatedOnConnectTest(fixtures.TestBase):
__requires__ = ("sqlite",)
def setUp(self):
e = create_engine("sqlite://")
connection = Mock(get_server_version_info=Mock(return_value="5.0"))
def connect(*args, **kwargs):
return connection
dbapi = Mock(
sqlite_version_info=(99, 9, 9),
version_info=(99, 9, 9),
sqlite_version="99.9.9",
paramstyle="named",
connect=Mock(side_effect=connect),
)
sqlite3 = e.dialect.dbapi
dbapi.Error = (sqlite3.Error,)
dbapi.ProgrammingError = sqlite3.ProgrammingError
self.dbapi = dbapi
self.ProgrammingError = sqlite3.ProgrammingError
def test_dont_touch_non_dbapi_exception_on_contextual_connect(self):
dbapi = self.dbapi
dbapi.connect = Mock(side_effect=TypeError("I'm not a DBAPI error"))
e = create_engine("sqlite://", module=dbapi)
e.dialect.is_disconnect = is_disconnect = Mock()
with testing.expect_deprecated(
r"The Engine.contextual_connect\(\) method is deprecated"
):
assert_raises_message(
TypeError, "I'm not a DBAPI error", e.contextual_connect
)
eq_(is_disconnect.call_count, 0)
def test_invalidate_on_contextual_connect(self):
"""test that is_disconnect() is called during connect.
interpretation of connection failures are not supported by
every backend.
"""
dbapi = self.dbapi
dbapi.connect = Mock(
side_effect=self.ProgrammingError(
"Cannot operate on a closed database."
)
)
e = create_engine("sqlite://", module=dbapi)
try:
with testing.expect_deprecated(
r"The Engine.contextual_connect\(\) method is deprecated"
):
e.contextual_connect()
assert False
except tsa.exc.DBAPIError as de:
assert de.connection_invalidated
class HandleErrorTest(fixtures.TestBase):
__requires__ = ("ad_hoc_engines",)
__backend__ = True
def tearDown(self):
Engine.dispatch._clear()
Engine._has_events = False
def test_legacy_dbapi_error(self):
engine = engines.testing_engine()
canary = Mock()
with testing.expect_deprecated(
r"The ConnectionEvents.dbapi_error\(\) event is deprecated"
):
event.listen(engine, "dbapi_error", canary)
with engine.connect() as conn:
try:
conn.execute("SELECT FOO FROM I_DONT_EXIST")
assert False
except tsa.exc.DBAPIError as e:
eq_(canary.mock_calls[0][1][5], e.orig)
eq_(canary.mock_calls[0][1][2], "SELECT FOO FROM I_DONT_EXIST")
def test_legacy_dbapi_error_no_ad_hoc_context(self):
engine = engines.testing_engine()
listener = Mock(return_value=None)
with testing.expect_deprecated(
r"The ConnectionEvents.dbapi_error\(\) event is deprecated"
):
event.listen(engine, "dbapi_error", listener)
nope = SomeException("nope")
class MyType(TypeDecorator):
impl = Integer
def process_bind_param(self, value, dialect):
raise nope
with engine.connect() as conn:
assert_raises_message(
tsa.exc.StatementError,
r"\(.*SomeException\) " r"nope\n\[SQL\: u?SELECT 1 ",
conn.execute,
select([1]).where(column("foo") == literal("bar", MyType())),
)
# no legacy event
eq_(listener.mock_calls, [])
def test_legacy_dbapi_error_non_dbapi_error(self):
engine = engines.testing_engine()
listener = Mock(return_value=None)
with testing.expect_deprecated(
r"The ConnectionEvents.dbapi_error\(\) event is deprecated"
):
event.listen(engine, "dbapi_error", listener)
nope = TypeError("I'm not a DBAPI error")
with engine.connect() as c:
c.connection.cursor = Mock(
return_value=Mock(execute=Mock(side_effect=nope))
)
assert_raises_message(
TypeError, "I'm not a DBAPI error", c.execute, "select "
)
# no legacy event
eq_(listener.mock_calls, [])
def MockDBAPI(): # noqa
def cursor():
return Mock()
def connect(*arg, **kw):
def close():
conn.closed = True
# mock seems like it might have an issue logging
# call_count correctly under threading, not sure.
# adding a side_effect for close seems to help.
conn = Mock(
cursor=Mock(side_effect=cursor),
close=Mock(side_effect=close),
closed=False,
)
return conn
def shutdown(value):
if value:
db.connect = Mock(side_effect=Exception("connect failed"))
else:
db.connect = Mock(side_effect=connect)
db.is_shutdown = value
db = Mock(
connect=Mock(side_effect=connect), shutdown=shutdown, is_shutdown=False
)
return db
class PoolTestBase(fixtures.TestBase):
def setup(self):
pool.clear_managers()
self._teardown_conns = []
def teardown(self):
for ref in self._teardown_conns:
conn = ref()
if conn:
conn.close()
@classmethod
def teardown_class(cls):
pool.clear_managers()
def _queuepool_fixture(self, **kw):
dbapi, pool = self._queuepool_dbapi_fixture(**kw)
return pool
def _queuepool_dbapi_fixture(self, **kw):
dbapi = MockDBAPI()
return (
dbapi,
pool.QueuePool(creator=lambda: dbapi.connect("foo.db"), **kw),
)
class DeprecatedPoolListenerTest(PoolTestBase):
@testing.requires.predictable_gc
@testing.uses_deprecated(
r".*Use the PoolEvents", r".*'listeners' argument .* is deprecated"
)
def test_listeners(self):
class InstrumentingListener(object):
def __init__(self):
if hasattr(self, "connect"):
self.connect = self.inst_connect
if hasattr(self, "first_connect"):
self.first_connect = self.inst_first_connect
if hasattr(self, "checkout"):
self.checkout = self.inst_checkout
if hasattr(self, "checkin"):
self.checkin = self.inst_checkin
self.clear()
def clear(self):
self.connected = []
self.first_connected = []
self.checked_out = []
self.checked_in = []
def assert_total(self, conn, fconn, cout, cin):
eq_(len(self.connected), conn)
eq_(len(self.first_connected), fconn)
eq_(len(self.checked_out), cout)
eq_(len(self.checked_in), cin)
def assert_in(self, item, in_conn, in_fconn, in_cout, in_cin):
eq_((item in self.connected), in_conn)
eq_((item in self.first_connected), in_fconn)
eq_((item in self.checked_out), in_cout)
eq_((item in self.checked_in), in_cin)
def inst_connect(self, con, record):
print("connect(%s, %s)" % (con, record))
assert con is not None
assert record is not None
self.connected.append(con)
def inst_first_connect(self, con, record):
print("first_connect(%s, %s)" % (con, record))
assert con is not None
assert record is not None
self.first_connected.append(con)
def inst_checkout(self, con, record, proxy):
print("checkout(%s, %s, %s)" % (con, record, proxy))
assert con is not None
assert record is not None
assert proxy is not None
self.checked_out.append(con)
def inst_checkin(self, con, record):
print("checkin(%s, %s)" % (con, record))
# con can be None if invalidated
assert record is not None
self.checked_in.append(con)
class ListenAll(tsa.interfaces.PoolListener, InstrumentingListener):
pass
class ListenConnect(InstrumentingListener):
def connect(self, con, record):
pass
class ListenFirstConnect(InstrumentingListener):
def first_connect(self, con, record):
pass
class ListenCheckOut(InstrumentingListener):
def checkout(self, con, record, proxy, num):
pass
class ListenCheckIn(InstrumentingListener):
def checkin(self, con, record):
pass
def assert_listeners(p, total, conn, fconn, cout, cin):
for instance in (p, p.recreate()):
self.assert_(len(instance.dispatch.connect) == conn)
self.assert_(len(instance.dispatch.first_connect) == fconn)
self.assert_(len(instance.dispatch.checkout) == cout)
self.assert_(len(instance.dispatch.checkin) == cin)
p = self._queuepool_fixture()
assert_listeners(p, 0, 0, 0, 0, 0)
with testing.expect_deprecated(
*[
"PoolListener.%s is deprecated." % name
for name in ["connect", "first_connect", "checkout", "checkin"]
]
):
p.add_listener(ListenAll())
assert_listeners(p, 1, 1, 1, 1, 1)
with testing.expect_deprecated(
*["PoolListener.%s is deprecated." % name for name in ["connect"]]
):
p.add_listener(ListenConnect())
assert_listeners(p, 2, 2, 1, 1, 1)
with testing.expect_deprecated(
*[
"PoolListener.%s is deprecated." % name
for name in ["first_connect"]
]
):
p.add_listener(ListenFirstConnect())
assert_listeners(p, 3, 2, 2, 1, 1)
with testing.expect_deprecated(
*["PoolListener.%s is deprecated." % name for name in ["checkout"]]
):
p.add_listener(ListenCheckOut())
assert_listeners(p, 4, 2, 2, 2, 1)
with testing.expect_deprecated(
*["PoolListener.%s is deprecated." % name for name in ["checkin"]]
):
p.add_listener(ListenCheckIn())
assert_listeners(p, 5, 2, 2, 2, 2)
del p
snoop = ListenAll()
with testing.expect_deprecated(
*[
"PoolListener.%s is deprecated." % name
for name in ["connect", "first_connect", "checkout", "checkin"]
]
+ [
"PoolListener is deprecated in favor of the PoolEvents "
"listener interface. The Pool.listeners parameter "
"will be removed"
]
):
p = self._queuepool_fixture(listeners=[snoop])
assert_listeners(p, 1, 1, 1, 1, 1)
c = p.connect()
snoop.assert_total(1, 1, 1, 0)
cc = c.connection
snoop.assert_in(cc, True, True, True, False)
c.close()
snoop.assert_in(cc, True, True, True, True)
del c, cc
snoop.clear()
# this one depends on immediate gc
c = p.connect()
cc = c.connection
snoop.assert_in(cc, False, False, True, False)
snoop.assert_total(0, 0, 1, 0)
del c, cc
lazy_gc()
snoop.assert_total(0, 0, 1, 1)
p.dispose()
snoop.clear()
c = p.connect()
c.close()
c = p.connect()
snoop.assert_total(1, 0, 2, 1)
c.close()
snoop.assert_total(1, 0, 2, 2)
# invalidation
p.dispose()
snoop.clear()
c = p.connect()
snoop.assert_total(1, 0, 1, 0)
c.invalidate()
snoop.assert_total(1, 0, 1, 1)
c.close()
snoop.assert_total(1, 0, 1, 1)
del c
lazy_gc()
snoop.assert_total(1, 0, 1, 1)
c = p.connect()
snoop.assert_total(2, 0, 2, 1)
c.close()
del c
lazy_gc()
snoop.assert_total(2, 0, 2, 2)
# detached
p.dispose()
snoop.clear()
c = p.connect()
snoop.assert_total(1, 0, 1, 0)
c.detach()
snoop.assert_total(1, 0, 1, 0)
c.close()
del c
snoop.assert_total(1, 0, 1, 0)
c = p.connect()
snoop.assert_total(2, 0, 2, 0)
c.close()
del c
snoop.assert_total(2, 0, 2, 1)
# recreated
p = p.recreate()
snoop.clear()
c = p.connect()
snoop.assert_total(1, 1, 1, 0)
c.close()
snoop.assert_total(1, 1, 1, 1)
c = p.connect()
snoop.assert_total(1, 1, 2, 1)
c.close()
snoop.assert_total(1, 1, 2, 2)
@testing.uses_deprecated(r".*Use the PoolEvents")
def test_listeners_callables(self):
def connect(dbapi_con, con_record):
counts[0] += 1
def checkout(dbapi_con, con_record, con_proxy):
counts[1] += 1
def checkin(dbapi_con, con_record):
counts[2] += 1
i_all = dict(connect=connect, checkout=checkout, checkin=checkin)
i_connect = dict(connect=connect)
i_checkout = dict(checkout=checkout)
i_checkin = dict(checkin=checkin)
for cls in (pool.QueuePool, pool.StaticPool):
counts = [0, 0, 0]
def assert_listeners(p, total, conn, cout, cin):
for instance in (p, p.recreate()):
eq_(len(instance.dispatch.connect), conn)
eq_(len(instance.dispatch.checkout), cout)
eq_(len(instance.dispatch.checkin), cin)
p = self._queuepool_fixture()
assert_listeners(p, 0, 0, 0, 0)
with testing.expect_deprecated(
*[
"PoolListener.%s is deprecated." % name
for name in ["connect", "checkout", "checkin"]
]
):
p.add_listener(i_all)
assert_listeners(p, 1, 1, 1, 1)
with testing.expect_deprecated(
*[
"PoolListener.%s is deprecated." % name
for name in ["connect"]
]
):
p.add_listener(i_connect)
assert_listeners(p, 2, 1, 1, 1)
with testing.expect_deprecated(
*[
"PoolListener.%s is deprecated." % name
for name in ["checkout"]
]
):
p.add_listener(i_checkout)
assert_listeners(p, 3, 1, 1, 1)
with testing.expect_deprecated(
*[
"PoolListener.%s is deprecated." % name
for name in ["checkin"]
]
):
p.add_listener(i_checkin)
assert_listeners(p, 4, 1, 1, 1)
del p
with testing.expect_deprecated(
*[
"PoolListener.%s is deprecated." % name
for name in ["connect", "checkout", "checkin"]
]
+ [".*The Pool.listeners parameter will be removed"]
):
p = self._queuepool_fixture(listeners=[i_all])
assert_listeners(p, 1, 1, 1, 1)
c = p.connect()
assert counts == [1, 1, 0]
c.close()
assert counts == [1, 1, 1]
c = p.connect()
assert counts == [1, 2, 1]
with testing.expect_deprecated(
*[
"PoolListener.%s is deprecated." % name
for name in ["checkin"]
]
):
p.add_listener(i_checkin)
c.close()
assert counts == [1, 2, 2]
class PoolTest(PoolTestBase):
def test_manager(self):
with testing.expect_deprecated(
r"The pool.manage\(\) function is deprecated,"
):
manager = pool.manage(MockDBAPI(), use_threadlocal=True)
with testing.expect_deprecated(
r".*Pool.use_threadlocal parameter is deprecated"
):
c1 = manager.connect("foo.db")
c2 = manager.connect("foo.db")
c3 = manager.connect("bar.db")
c4 = manager.connect("foo.db", bar="bat")
c5 = manager.connect("foo.db", bar="hoho")
c6 = manager.connect("foo.db", bar="bat")
assert c1.cursor() is not None
assert c1 is c2
assert c1 is not c3
assert c4 is c6
assert c4 is not c5
def test_manager_with_key(self):
dbapi = MockDBAPI()
with testing.expect_deprecated(
r"The pool.manage\(\) function is deprecated,"
):
manager = pool.manage(dbapi, use_threadlocal=True)
with testing.expect_deprecated(
r".*Pool.use_threadlocal parameter is deprecated"
):
c1 = manager.connect("foo.db", sa_pool_key="a")
c2 = manager.connect("foo.db", sa_pool_key="b")
c3 = manager.connect("bar.db", sa_pool_key="a")
assert c1.cursor() is not None
assert c1 is not c2
assert c1 is c3
eq_(dbapi.connect.mock_calls, [call("foo.db"), call("foo.db")])
def test_bad_args(self):
with testing.expect_deprecated(
r"The pool.manage\(\) function is deprecated,"
):
manager = pool.manage(MockDBAPI())
manager.connect(None)
def test_non_thread_local_manager(self):
with testing.expect_deprecated(
r"The pool.manage\(\) function is deprecated,"
):
manager = pool.manage(MockDBAPI(), use_threadlocal=False)
connection = manager.connect("foo.db")
connection2 = manager.connect("foo.db")
self.assert_(connection.cursor() is not None)
self.assert_(connection is not connection2)
def test_threadlocal_del(self):
self._do_testthreadlocal(useclose=False)
def test_threadlocal_close(self):
self._do_testthreadlocal(useclose=True)
def _do_testthreadlocal(self, useclose=False):
dbapi = MockDBAPI()
with testing.expect_deprecated(
r".*Pool.use_threadlocal parameter is deprecated"
):
for p in (
pool.QueuePool(
creator=dbapi.connect,
pool_size=3,
max_overflow=-1,
use_threadlocal=True,
),
pool.SingletonThreadPool(
creator=dbapi.connect, use_threadlocal=True
),
):
c1 = p.connect()
c2 = p.connect()
self.assert_(c1 is c2)
c3 = p.unique_connection()
self.assert_(c3 is not c1)
if useclose:
c2.close()
else:
c2 = None
c2 = p.connect()
self.assert_(c1 is c2)
self.assert_(c3 is not c1)
if useclose:
c2.close()
else:
c2 = None
lazy_gc()
if useclose:
c1 = p.connect()
c2 = p.connect()
c3 = p.connect()
c3.close()
c2.close()
self.assert_(c1.connection is not None)
c1.close()
c1 = c2 = c3 = None
# extra tests with QueuePool to ensure connections get
# __del__()ed when dereferenced
if isinstance(p, pool.QueuePool):
lazy_gc()
self.assert_(p.checkedout() == 0)
c1 = p.connect()
c2 = p.connect()
if useclose:
c2.close()
c1.close()
else:
c2 = None
c1 = None
lazy_gc()
self.assert_(p.checkedout() == 0)
def test_mixed_close(self):
pool._refs.clear()
with testing.expect_deprecated(
r".*Pool.use_threadlocal parameter is deprecated"
):
p = self._queuepool_fixture(
pool_size=3, max_overflow=-1, use_threadlocal=True
)
c1 = p.connect()
c2 = p.connect()
assert c1 is c2
c1.close()
c2 = None
assert p.checkedout() == 1
c1 = None
lazy_gc()
assert p.checkedout() == 0
lazy_gc()
assert not pool._refs
class QueuePoolTest(PoolTestBase):
def test_threadfairy(self):
with testing.expect_deprecated(
r".*Pool.use_threadlocal parameter is deprecated"
):
p = self._queuepool_fixture(
pool_size=3, max_overflow=-1, use_threadlocal=True
)
c1 = p.connect()
c1.close()
c2 = p.connect()
assert c2.connection is not None
def test_trick_the_counter(self):
"""this is a "flaw" in the connection pool; since threadlocal
uses a single ConnectionFairy per thread with an open/close
counter, you can fool the counter into giving you a
ConnectionFairy with an ambiguous counter. i.e. its not true
reference counting."""
with testing.expect_deprecated(
r".*Pool.use_threadlocal parameter is deprecated"
):
p = self._queuepool_fixture(
pool_size=3, max_overflow=-1, use_threadlocal=True
)
c1 = p.connect()
c2 = p.connect()
assert c1 is c2
c1.close()
c2 = p.connect()
c2.close()
self.assert_(p.checkedout() != 0)
c2.close()
self.assert_(p.checkedout() == 0)
@testing.requires.predictable_gc
def test_weakref_kaboom(self):
with testing.expect_deprecated(
r".*Pool.use_threadlocal parameter is deprecated"
):
p = self._queuepool_fixture(
pool_size=3, max_overflow=-1, use_threadlocal=True
)
c1 = p.connect()
c2 = p.connect()
c1.close()
c2 = None
del c1
del c2
gc_collect()
assert p.checkedout() == 0
c3 = p.connect()
assert c3 is not None
class ExplicitAutoCommitDeprecatedTest(fixtures.TestBase):
"""test the 'autocommit' flag on select() and text() objects.
Requires PostgreSQL so that we may define a custom function which
modifies the database. """
__only_on__ = "postgresql"
@classmethod
def setup_class(cls):
global metadata, foo
metadata = MetaData(testing.db)
foo = Table(
"foo",
metadata,
Column("id", Integer, primary_key=True),
Column("data", String(100)),
)
metadata.create_all()
testing.db.execute(
"create function insert_foo(varchar) "
"returns integer as 'insert into foo(data) "
"values ($1);select 1;' language sql"
)
def teardown(self):
foo.delete().execute().close()
@classmethod
def teardown_class(cls):
testing.db.execute("drop function insert_foo(varchar)")
metadata.drop_all()
def test_explicit_compiled(self):
conn1 = testing.db.connect()
conn2 = testing.db.connect()
with testing.expect_deprecated(
"The select.autocommit parameter is deprecated"
):
conn1.execute(select([func.insert_foo("data1")], autocommit=True))
assert conn2.execute(select([foo.c.data])).fetchall() == [("data1",)]
with testing.expect_deprecated(
r"The SelectBase.autocommit\(\) method is deprecated,"
):
conn1.execute(select([func.insert_foo("data2")]).autocommit())
assert conn2.execute(select([foo.c.data])).fetchall() == [
("data1",),
("data2",),
]
conn1.close()
conn2.close()
def test_explicit_text(self):
conn1 = testing.db.connect()
conn2 = testing.db.connect()
with testing.expect_deprecated(
"The text.autocommit parameter is deprecated"
):
conn1.execute(
text("select insert_foo('moredata')", autocommit=True)
)
assert conn2.execute(select([foo.c.data])).fetchall() == [
("moredata",)
]
conn1.close()
conn2.close()