summaryrefslogtreecommitdiff
path: root/test
diff options
context:
space:
mode:
Diffstat (limited to 'test')
-rw-r--r--test/base/test_events.py126
-rw-r--r--test/base/test_utils.py222
-rw-r--r--test/ext/asyncio/test_engine_py3k.py182
-rw-r--r--test/ext/asyncio/test_session_py3k.py58
-rw-r--r--test/orm/test_scoping.py24
-rw-r--r--test/orm/test_session.py55
6 files changed, 500 insertions, 167 deletions
diff --git a/test/base/test_events.py b/test/base/test_events.py
index a4ed1000b..19f68e9a3 100644
--- a/test/base/test_events.py
+++ b/test/base/test_events.py
@@ -15,7 +15,19 @@ from sqlalchemy.testing.mock import Mock
from sqlalchemy.testing.util import gc_collect
-class EventsTest(fixtures.TestBase):
+class TearDownLocalEventsFixture(object):
+ def tearDown(self):
+ classes = set()
+ for entry in event.base._registrars.values():
+ for evt_cls in entry:
+ if evt_cls.__module__ == __name__:
+ classes.add(evt_cls)
+
+ for evt_cls in classes:
+ event.base._remove_dispatcher(evt_cls)
+
+
+class EventsTest(TearDownLocalEventsFixture, fixtures.TestBase):
"""Test class- and instance-level event registration."""
def setUp(self):
@@ -34,9 +46,6 @@ class EventsTest(fixtures.TestBase):
self.Target = Target
- def tearDown(self):
- event.base._remove_dispatcher(self.Target.__dict__["dispatch"].events)
-
def test_register_class(self):
def listen(x, y):
pass
@@ -258,7 +267,60 @@ class EventsTest(fixtures.TestBase):
)
-class NamedCallTest(fixtures.TestBase):
+class SlotsEventsTest(fixtures.TestBase):
+ @testing.requires.python3
+ def test_no_slots_dispatch(self):
+ class Target(object):
+ __slots__ = ()
+
+ class TargetEvents(event.Events):
+ _dispatch_target = Target
+
+ def event_one(self, x, y):
+ pass
+
+ def event_two(self, x):
+ pass
+
+ def event_three(self, x):
+ pass
+
+ t1 = Target()
+
+ with testing.expect_raises_message(
+ TypeError,
+ r"target .*Target.* doesn't have __dict__, should it "
+ "be defining _slots_dispatch",
+ ):
+ event.listen(t1, "event_one", Mock())
+
+ def test_slots_dispatch(self):
+ class Target(object):
+ __slots__ = ("_slots_dispatch",)
+
+ class TargetEvents(event.Events):
+ _dispatch_target = Target
+
+ def event_one(self, x, y):
+ pass
+
+ def event_two(self, x):
+ pass
+
+ def event_three(self, x):
+ pass
+
+ t1 = Target()
+
+ m1 = Mock()
+ event.listen(t1, "event_one", m1)
+
+ t1.dispatch.event_one(2, 4)
+
+ eq_(m1.mock_calls, [call(2, 4)])
+
+
+class NamedCallTest(TearDownLocalEventsFixture, fixtures.TestBase):
def _fixture(self):
class TargetEventsOne(event.Events):
def event_one(self, x, y):
@@ -373,7 +435,7 @@ class NamedCallTest(fixtures.TestBase):
eq_(canary.mock_calls, [call({"x": 4, "y": 5, "z": 8, "q": 5})])
-class LegacySignatureTest(fixtures.TestBase):
+class LegacySignatureTest(TearDownLocalEventsFixture, fixtures.TestBase):
"""test adaption of legacy args"""
def setUp(self):
@@ -397,11 +459,6 @@ class LegacySignatureTest(fixtures.TestBase):
self.TargetOne = TargetOne
- def tearDown(self):
- event.base._remove_dispatcher(
- self.TargetOne.__dict__["dispatch"].events
- )
-
def test_legacy_accept(self):
canary = Mock()
@@ -550,12 +607,7 @@ class LegacySignatureTest(fixtures.TestBase):
)
-class ClsLevelListenTest(fixtures.TestBase):
- def tearDown(self):
- event.base._remove_dispatcher(
- self.TargetOne.__dict__["dispatch"].events
- )
-
+class ClsLevelListenTest(TearDownLocalEventsFixture, fixtures.TestBase):
def setUp(self):
class TargetEventsOne(event.Events):
def event_one(self, x, y):
@@ -622,7 +674,7 @@ class ClsLevelListenTest(fixtures.TestBase):
assert handler2 not in s2.dispatch.event_one
-class AcceptTargetsTest(fixtures.TestBase):
+class AcceptTargetsTest(TearDownLocalEventsFixture, fixtures.TestBase):
"""Test default target acceptance."""
def setUp(self):
@@ -643,14 +695,6 @@ class AcceptTargetsTest(fixtures.TestBase):
self.TargetOne = TargetOne
self.TargetTwo = TargetTwo
- def tearDown(self):
- event.base._remove_dispatcher(
- self.TargetOne.__dict__["dispatch"].events
- )
- event.base._remove_dispatcher(
- self.TargetTwo.__dict__["dispatch"].events
- )
-
def test_target_accept(self):
"""Test that events of the same name are routed to the correct
collection based on the type of target given.
@@ -687,7 +731,7 @@ class AcceptTargetsTest(fixtures.TestBase):
eq_(list(t2.dispatch.event_one), [listen_two, listen_four])
-class CustomTargetsTest(fixtures.TestBase):
+class CustomTargetsTest(TearDownLocalEventsFixture, fixtures.TestBase):
"""Test custom target acceptance."""
def setUp(self):
@@ -707,9 +751,6 @@ class CustomTargetsTest(fixtures.TestBase):
self.Target = Target
- def tearDown(self):
- event.base._remove_dispatcher(self.Target.__dict__["dispatch"].events)
-
def test_indirect(self):
def listen(x, y):
pass
@@ -727,7 +768,7 @@ class CustomTargetsTest(fixtures.TestBase):
)
-class SubclassGrowthTest(fixtures.TestBase):
+class SubclassGrowthTest(TearDownLocalEventsFixture, fixtures.TestBase):
"""test that ad-hoc subclasses are garbage collected."""
def setUp(self):
@@ -752,7 +793,7 @@ class SubclassGrowthTest(fixtures.TestBase):
eq_(self.Target.__subclasses__(), [])
-class ListenOverrideTest(fixtures.TestBase):
+class ListenOverrideTest(TearDownLocalEventsFixture, fixtures.TestBase):
"""Test custom listen functions which change the listener function
signature."""
@@ -778,9 +819,6 @@ class ListenOverrideTest(fixtures.TestBase):
self.Target = Target
- def tearDown(self):
- event.base._remove_dispatcher(self.Target.__dict__["dispatch"].events)
-
def test_listen_override(self):
listen_one = Mock()
listen_two = Mock()
@@ -816,7 +854,7 @@ class ListenOverrideTest(fixtures.TestBase):
eq_(listen_one.mock_calls, [call(12)])
-class PropagateTest(fixtures.TestBase):
+class PropagateTest(TearDownLocalEventsFixture, fixtures.TestBase):
def setUp(self):
class TargetEvents(event.Events):
def event_one(self, arg):
@@ -850,7 +888,7 @@ class PropagateTest(fixtures.TestBase):
eq_(listen_two.mock_calls, [])
-class JoinTest(fixtures.TestBase):
+class JoinTest(TearDownLocalEventsFixture, fixtures.TestBase):
def setUp(self):
class TargetEvents(event.Events):
def event_one(self, target, arg):
@@ -875,11 +913,6 @@ class JoinTest(fixtures.TestBase):
self.TargetFactory = TargetFactory
self.TargetElement = TargetElement
- def tearDown(self):
- for cls in (self.TargetElement, self.TargetFactory, self.BaseTarget):
- if "dispatch" in cls.__dict__:
- event.base._remove_dispatcher(cls.__dict__["dispatch"].events)
-
def test_neither(self):
element = self.TargetFactory().create()
element.run_event(1)
@@ -1075,7 +1108,7 @@ class JoinTest(fixtures.TestBase):
)
-class DisableClsPropagateTest(fixtures.TestBase):
+class DisableClsPropagateTest(TearDownLocalEventsFixture, fixtures.TestBase):
def setUp(self):
class TargetEvents(event.Events):
def event_one(self, target, arg):
@@ -1093,11 +1126,6 @@ class DisableClsPropagateTest(fixtures.TestBase):
self.BaseTarget = BaseTarget
self.SubTarget = SubTarget
- def tearDown(self):
- for cls in (self.SubTarget, self.BaseTarget):
- if "dispatch" in cls.__dict__:
- event.base._remove_dispatcher(cls.__dict__["dispatch"].events)
-
def test_listen_invoke_clslevel(self):
canary = Mock()
@@ -1132,7 +1160,7 @@ class DisableClsPropagateTest(fixtures.TestBase):
eq_(canary.mock_calls, [])
-class RemovalTest(fixtures.TestBase):
+class RemovalTest(TearDownLocalEventsFixture, fixtures.TestBase):
def _fixture(self):
class TargetEvents(event.Events):
def event_one(self, x, y):
diff --git a/test/base/test_utils.py b/test/base/test_utils.py
index fa347243e..9220720b6 100644
--- a/test/base/test_utils.py
+++ b/test/base/test_utils.py
@@ -2300,7 +2300,14 @@ class SymbolTest(fixtures.TestBase):
class _Py3KFixtures(object):
- pass
+ def _kw_only_fixture(self):
+ pass
+
+ def _kw_plus_posn_fixture(self):
+ pass
+
+ def _kw_opt_fixture(self):
+ pass
if util.py3k:
@@ -2321,185 +2328,208 @@ def _kw_opt_fixture(self, a, *, b, c="c"):
for k in _locals:
setattr(_Py3KFixtures, k, _locals[k])
+py3k_fixtures = _Py3KFixtures()
-class TestFormatArgspec(_Py3KFixtures, fixtures.TestBase):
- def _test_format_argspec_plus(self, fn, wanted, grouped=None):
-
- # test direct function
- if grouped is None:
- parsed = util.format_argspec_plus(fn)
- else:
- parsed = util.format_argspec_plus(fn, grouped=grouped)
- eq_(parsed, wanted)
-
- # test sending fullargspec
- spec = compat.inspect_getfullargspec(fn)
- if grouped is None:
- parsed = util.format_argspec_plus(spec)
- else:
- parsed = util.format_argspec_plus(spec, grouped=grouped)
- eq_(parsed, wanted)
- def test_specs(self):
- self._test_format_argspec_plus(
+class TestFormatArgspec(_Py3KFixtures, fixtures.TestBase):
+ @testing.combinations(
+ (
lambda: None,
{
"args": "()",
"self_arg": None,
"apply_kw": "()",
"apply_pos": "()",
+ "apply_pos_proxied": "()",
},
- )
-
- self._test_format_argspec_plus(
+ True,
+ ),
+ (
lambda: None,
- {"args": "", "self_arg": None, "apply_kw": "", "apply_pos": ""},
- grouped=False,
- )
-
- self._test_format_argspec_plus(
+ {
+ "args": "",
+ "self_arg": None,
+ "apply_kw": "",
+ "apply_pos": "",
+ "apply_pos_proxied": "",
+ },
+ False,
+ ),
+ (
lambda self: None,
{
"args": "(self)",
"self_arg": "self",
"apply_kw": "(self)",
"apply_pos": "(self)",
+ "apply_pos_proxied": "()",
},
- )
-
- self._test_format_argspec_plus(
+ True,
+ ),
+ (
lambda self: None,
{
"args": "self",
"self_arg": "self",
"apply_kw": "self",
"apply_pos": "self",
+ "apply_pos_proxied": "",
},
- grouped=False,
- )
-
- self._test_format_argspec_plus(
+ False,
+ ),
+ (
lambda *a: None,
{
"args": "(*a)",
"self_arg": "a[0]",
"apply_kw": "(*a)",
"apply_pos": "(*a)",
+ "apply_pos_proxied": "(*a)",
},
- )
-
- self._test_format_argspec_plus(
+ True,
+ ),
+ (
lambda **kw: None,
{
"args": "(**kw)",
"self_arg": None,
"apply_kw": "(**kw)",
"apply_pos": "(**kw)",
+ "apply_pos_proxied": "(**kw)",
},
- )
-
- self._test_format_argspec_plus(
+ True,
+ ),
+ (
lambda *a, **kw: None,
{
"args": "(*a, **kw)",
"self_arg": "a[0]",
"apply_kw": "(*a, **kw)",
"apply_pos": "(*a, **kw)",
+ "apply_pos_proxied": "(*a, **kw)",
},
- )
-
- self._test_format_argspec_plus(
+ True,
+ ),
+ (
lambda a, *b: None,
{
"args": "(a, *b)",
"self_arg": "a",
"apply_kw": "(a, *b)",
"apply_pos": "(a, *b)",
+ "apply_pos_proxied": "(*b)",
},
- )
-
- self._test_format_argspec_plus(
+ True,
+ ),
+ (
lambda a, **b: None,
{
"args": "(a, **b)",
"self_arg": "a",
"apply_kw": "(a, **b)",
"apply_pos": "(a, **b)",
+ "apply_pos_proxied": "(**b)",
},
- )
-
- self._test_format_argspec_plus(
+ True,
+ ),
+ (
lambda a, *b, **c: None,
{
"args": "(a, *b, **c)",
"self_arg": "a",
"apply_kw": "(a, *b, **c)",
"apply_pos": "(a, *b, **c)",
+ "apply_pos_proxied": "(*b, **c)",
},
- )
-
- self._test_format_argspec_plus(
+ True,
+ ),
+ (
lambda a, b=1, **c: None,
{
"args": "(a, b=1, **c)",
"self_arg": "a",
"apply_kw": "(a, b=b, **c)",
"apply_pos": "(a, b, **c)",
+ "apply_pos_proxied": "(b, **c)",
},
- )
-
- self._test_format_argspec_plus(
+ True,
+ ),
+ (
lambda a=1, b=2: None,
{
"args": "(a=1, b=2)",
"self_arg": "a",
"apply_kw": "(a=a, b=b)",
"apply_pos": "(a, b)",
+ "apply_pos_proxied": "(b)",
},
- )
-
- self._test_format_argspec_plus(
+ True,
+ ),
+ (
lambda a=1, b=2: None,
{
"args": "a=1, b=2",
"self_arg": "a",
"apply_kw": "a=a, b=b",
"apply_pos": "a, b",
+ "apply_pos_proxied": "b",
},
- grouped=False,
- )
-
- if util.py3k:
- self._test_format_argspec_plus(
- self._kw_only_fixture,
- {
- "args": "self, a, *, b, c",
- "self_arg": "self",
- "apply_pos": "self, a, *, b, c",
- "apply_kw": "self, a, b=b, c=c",
- },
- grouped=False,
- )
- self._test_format_argspec_plus(
- self._kw_plus_posn_fixture,
- {
- "args": "self, a, *args, b, c",
- "self_arg": "self",
- "apply_pos": "self, a, *args, b, c",
- "apply_kw": "self, a, b=b, c=c, *args",
- },
- grouped=False,
- )
- self._test_format_argspec_plus(
- self._kw_opt_fixture,
- {
- "args": "self, a, *, b, c='c'",
- "self_arg": "self",
- "apply_pos": "self, a, *, b, c",
- "apply_kw": "self, a, b=b, c=c",
- },
- grouped=False,
- )
+ False,
+ ),
+ (
+ py3k_fixtures._kw_only_fixture,
+ {
+ "args": "self, a, *, b, c",
+ "self_arg": "self",
+ "apply_pos": "self, a, *, b, c",
+ "apply_kw": "self, a, b=b, c=c",
+ "apply_pos_proxied": "a, *, b, c",
+ },
+ False,
+ testing.requires.python3,
+ ),
+ (
+ py3k_fixtures._kw_plus_posn_fixture,
+ {
+ "args": "self, a, *args, b, c",
+ "self_arg": "self",
+ "apply_pos": "self, a, *args, b, c",
+ "apply_kw": "self, a, b=b, c=c, *args",
+ "apply_pos_proxied": "a, *args, b, c",
+ },
+ False,
+ testing.requires.python3,
+ ),
+ (
+ py3k_fixtures._kw_opt_fixture,
+ {
+ "args": "self, a, *, b, c='c'",
+ "self_arg": "self",
+ "apply_pos": "self, a, *, b, c",
+ "apply_kw": "self, a, b=b, c=c",
+ "apply_pos_proxied": "a, *, b, c",
+ },
+ False,
+ testing.requires.python3,
+ ),
+ argnames="fn,wanted,grouped",
+ )
+ def test_specs(self, fn, wanted, grouped):
+
+ # test direct function
+ if grouped is None:
+ parsed = util.format_argspec_plus(fn)
+ else:
+ parsed = util.format_argspec_plus(fn, grouped=grouped)
+ eq_(parsed, wanted)
+
+ # test sending fullargspec
+ spec = compat.inspect_getfullargspec(fn)
+ if grouped is None:
+ parsed = util.format_argspec_plus(spec)
+ else:
+ parsed = util.format_argspec_plus(spec, grouped=grouped)
+ eq_(parsed, wanted)
@testing.requires.cpython
def test_init_grouped(self):
@@ -2508,17 +2538,20 @@ class TestFormatArgspec(_Py3KFixtures, fixtures.TestBase):
"self_arg": "self",
"apply_pos": "(self)",
"apply_kw": "(self)",
+ "apply_pos_proxied": "()",
}
wrapper_spec = {
"args": "(self, *args, **kwargs)",
"self_arg": "self",
"apply_pos": "(self, *args, **kwargs)",
"apply_kw": "(self, *args, **kwargs)",
+ "apply_pos_proxied": "(*args, **kwargs)",
}
custom_spec = {
"args": "(slef, a=123)",
"self_arg": "slef", # yes, slef
"apply_pos": "(slef, a)",
+ "apply_pos_proxied": "(a)",
"apply_kw": "(slef, a=a)",
}
@@ -2532,18 +2565,21 @@ class TestFormatArgspec(_Py3KFixtures, fixtures.TestBase):
"self_arg": "self",
"apply_pos": "self",
"apply_kw": "self",
+ "apply_pos_proxied": "",
}
wrapper_spec = {
"args": "self, *args, **kwargs",
"self_arg": "self",
"apply_pos": "self, *args, **kwargs",
"apply_kw": "self, *args, **kwargs",
+ "apply_pos_proxied": "*args, **kwargs",
}
custom_spec = {
"args": "slef, a=123",
"self_arg": "slef", # yes, slef
"apply_pos": "slef, a",
"apply_kw": "slef, a=a",
+ "apply_pos_proxied": "a",
}
self._test_init(False, object_spec, wrapper_spec, custom_spec)
diff --git a/test/ext/asyncio/test_engine_py3k.py b/test/ext/asyncio/test_engine_py3k.py
index 7c7d90e21..83987b06f 100644
--- a/test/ext/asyncio/test_engine_py3k.py
+++ b/test/ext/asyncio/test_engine_py3k.py
@@ -2,6 +2,7 @@ import asyncio
from sqlalchemy import Column
from sqlalchemy import delete
+from sqlalchemy import event
from sqlalchemy import exc
from sqlalchemy import func
from sqlalchemy import Integer
@@ -9,13 +10,19 @@ from sqlalchemy import select
from sqlalchemy import String
from sqlalchemy import Table
from sqlalchemy import testing
+from sqlalchemy import text
from sqlalchemy import union_all
from sqlalchemy.ext.asyncio import create_async_engine
+from sqlalchemy.ext.asyncio import engine as _async_engine
from sqlalchemy.ext.asyncio import exc as asyncio_exc
from sqlalchemy.testing import async_test
from sqlalchemy.testing import eq_
from sqlalchemy.testing import fixtures
+from sqlalchemy.testing import is_
+from sqlalchemy.testing import is_not
+from sqlalchemy.testing import mock
from sqlalchemy.testing.asyncio import assert_raises_message_async
+from sqlalchemy.util.concurrency import greenlet_spawn
class EngineFixture(fixtures.TablesTest):
@@ -50,6 +57,117 @@ class EngineFixture(fixtures.TablesTest):
class AsyncEngineTest(EngineFixture):
__backend__ = True
+ def test_proxied_attrs_engine(self, async_engine):
+ sync_engine = async_engine.sync_engine
+
+ is_(async_engine.url, sync_engine.url)
+ is_(async_engine.pool, sync_engine.pool)
+ is_(async_engine.dialect, sync_engine.dialect)
+ eq_(async_engine.name, sync_engine.name)
+ eq_(async_engine.driver, sync_engine.driver)
+ eq_(async_engine.echo, sync_engine.echo)
+
+ def test_clear_compiled_cache(self, async_engine):
+ async_engine.sync_engine._compiled_cache["foo"] = "bar"
+ eq_(async_engine.sync_engine._compiled_cache["foo"], "bar")
+ async_engine.clear_compiled_cache()
+ assert "foo" not in async_engine.sync_engine._compiled_cache
+
+ def test_execution_options(self, async_engine):
+ a2 = async_engine.execution_options(foo="bar")
+ assert isinstance(a2, _async_engine.AsyncEngine)
+ eq_(a2.sync_engine._execution_options, {"foo": "bar"})
+ eq_(async_engine.sync_engine._execution_options, {})
+
+ """
+
+ attr uri, pool, dialect, engine, name, driver, echo
+ methods clear_compiled_cache, update_execution_options,
+ execution_options, get_execution_options, dispose
+
+ """
+
+ @async_test
+ async def test_proxied_attrs_connection(self, async_engine):
+ conn = await async_engine.connect()
+
+ sync_conn = conn.sync_connection
+
+ is_(conn.engine, async_engine)
+ is_(conn.closed, sync_conn.closed)
+ is_(conn.dialect, async_engine.sync_engine.dialect)
+ eq_(conn.default_isolation_level, sync_conn.default_isolation_level)
+
+ @async_test
+ async def test_invalidate(self, async_engine):
+ conn = await async_engine.connect()
+
+ is_(conn.invalidated, False)
+
+ connection_fairy = await conn.get_raw_connection()
+ is_(connection_fairy.is_valid, True)
+ dbapi_connection = connection_fairy.connection
+
+ await conn.invalidate()
+ assert dbapi_connection._connection.is_closed()
+
+ new_fairy = await conn.get_raw_connection()
+ is_not(new_fairy.connection, dbapi_connection)
+ is_not(new_fairy, connection_fairy)
+ is_(new_fairy.is_valid, True)
+ is_(connection_fairy.is_valid, False)
+
+ @async_test
+ async def test_get_dbapi_connection_raise(self, async_engine):
+
+ conn = await async_engine.connect()
+
+ with testing.expect_raises_message(
+ exc.InvalidRequestError,
+ "AsyncConnection.connection accessor is not "
+ "implemented as the attribute",
+ ):
+ conn.connection
+
+ @async_test
+ async def test_get_raw_connection(self, async_engine):
+
+ conn = await async_engine.connect()
+
+ pooled = await conn.get_raw_connection()
+ is_(pooled, conn.sync_connection.connection)
+
+ @async_test
+ async def test_isolation_level(self, async_engine):
+ conn = await async_engine.connect()
+
+ sync_isolation_level = await greenlet_spawn(
+ conn.sync_connection.get_isolation_level
+ )
+ isolation_level = await conn.get_isolation_level()
+
+ eq_(isolation_level, sync_isolation_level)
+
+ await conn.execution_options(isolation_level="SERIALIZABLE")
+ isolation_level = await conn.get_isolation_level()
+
+ eq_(isolation_level, "SERIALIZABLE")
+
+ @async_test
+ async def test_dispose(self, async_engine):
+ c1 = await async_engine.connect()
+ c2 = await async_engine.connect()
+
+ await c1.close()
+ await c2.close()
+
+ p1 = async_engine.pool
+ eq_(async_engine.pool.checkedin(), 2)
+
+ await async_engine.dispose()
+ eq_(async_engine.pool.checkedin(), 0)
+ is_not(p1, async_engine.pool)
+
@async_test
async def test_init_once_concurrency(self, async_engine):
c1 = async_engine.connect()
@@ -169,6 +287,70 @@ class AsyncEngineTest(EngineFixture):
)
+class AsyncEventTest(EngineFixture):
+ """The engine events all run in their normal synchronous context.
+
+ we do not provide an asyncio event interface at this time.
+
+ """
+
+ __backend__ = True
+
+ @async_test
+ async def test_no_async_listeners(self, async_engine):
+ with testing.expect_raises_message(
+ NotImplementedError,
+ "asynchronous events are not implemented "
+ "at this time. Apply synchronous listeners to the "
+ "AsyncEngine.sync_engine or "
+ "AsyncConnection.sync_connection attributes.",
+ ):
+ event.listen(async_engine, "before_cursor_execute", mock.Mock())
+
+ conn = await async_engine.connect()
+
+ with testing.expect_raises_message(
+ NotImplementedError,
+ "asynchronous events are not implemented "
+ "at this time. Apply synchronous listeners to the "
+ "AsyncEngine.sync_engine or "
+ "AsyncConnection.sync_connection attributes.",
+ ):
+ event.listen(conn, "before_cursor_execute", mock.Mock())
+
+ @async_test
+ async def test_sync_before_cursor_execute_engine(self, async_engine):
+ canary = mock.Mock()
+
+ event.listen(async_engine.sync_engine, "before_cursor_execute", canary)
+
+ async with async_engine.connect() as conn:
+ sync_conn = conn.sync_connection
+ await conn.execute(text("select 1"))
+
+ eq_(
+ canary.mock_calls,
+ [mock.call(sync_conn, mock.ANY, "select 1", (), mock.ANY, False)],
+ )
+
+ @async_test
+ async def test_sync_before_cursor_execute_connection(self, async_engine):
+ canary = mock.Mock()
+
+ async with async_engine.connect() as conn:
+ sync_conn = conn.sync_connection
+
+ event.listen(
+ async_engine.sync_engine, "before_cursor_execute", canary
+ )
+ await conn.execute(text("select 1"))
+
+ eq_(
+ canary.mock_calls,
+ [mock.call(sync_conn, mock.ANY, "select 1", (), mock.ANY, False)],
+ )
+
+
class AsyncResultTest(EngineFixture):
@testing.combinations(
(None,), ("scalars",), ("mappings",), argnames="filter_"
diff --git a/test/ext/asyncio/test_session_py3k.py b/test/ext/asyncio/test_session_py3k.py
index e8caaca3e..a3b8add67 100644
--- a/test/ext/asyncio/test_session_py3k.py
+++ b/test/ext/asyncio/test_session_py3k.py
@@ -1,3 +1,4 @@
+from sqlalchemy import event
from sqlalchemy import exc
from sqlalchemy import func
from sqlalchemy import select
@@ -9,6 +10,7 @@ from sqlalchemy.orm import selectinload
from sqlalchemy.testing import async_test
from sqlalchemy.testing import eq_
from sqlalchemy.testing import is_
+from sqlalchemy.testing import mock
from ...orm import _fixtures
@@ -140,6 +142,27 @@ class AsyncSessionTransactionTest(AsyncFixture):
eq_(await outer_conn.scalar(select(func.count(User.id))), 1)
@async_test
+ async def test_delete(self, async_session):
+ User = self.classes.User
+
+ async with async_session.begin():
+ u1 = User(name="u1")
+
+ async_session.add(u1)
+
+ await async_session.flush()
+
+ conn = await async_session.connection()
+
+ eq_(await conn.scalar(select(func.count(User.id))), 1)
+
+ async_session.delete(u1)
+
+ await async_session.flush()
+
+ eq_(await conn.scalar(select(func.count(User.id))), 0)
+
+ @async_test
async def test_flush(self, async_session):
User = self.classes.User
@@ -198,3 +221,38 @@ class AsyncSessionTransactionTest(AsyncFixture):
is_(new_u_merged, u1)
eq_(u1.name, "new u1")
+
+
+class AsyncEventTest(AsyncFixture):
+ """The engine events all run in their normal synchronous context.
+
+ we do not provide an asyncio event interface at this time.
+
+ """
+
+ __backend__ = True
+
+ @async_test
+ async def test_no_async_listeners(self, async_session):
+ with testing.expect_raises(
+ NotImplementedError,
+ "NotImplementedError: asynchronous events are not implemented "
+ "at this time. Apply synchronous listeners to the "
+ "AsyncEngine.sync_engine or "
+ "AsyncConnection.sync_connection attributes.",
+ ):
+ event.listen(async_session, "before_flush", mock.Mock())
+
+ @async_test
+ async def test_sync_before_commit(self, async_session):
+ canary = mock.Mock()
+
+ event.listen(async_session.sync_session, "before_commit", canary)
+
+ async with async_session.begin():
+ pass
+
+ eq_(
+ canary.mock_calls,
+ [mock.call(async_session.sync_session)],
+ )
diff --git a/test/orm/test_scoping.py b/test/orm/test_scoping.py
index 6b7feaea7..d1ed9acc1 100644
--- a/test/orm/test_scoping.py
+++ b/test/orm/test_scoping.py
@@ -10,6 +10,7 @@ from sqlalchemy.orm import scoped_session
from sqlalchemy.testing import assert_raises_message
from sqlalchemy.testing import eq_
from sqlalchemy.testing import fixtures
+from sqlalchemy.testing import mock
from sqlalchemy.testing.mock import Mock
from sqlalchemy.testing.schema import Column
from sqlalchemy.testing.schema import Table
@@ -127,3 +128,26 @@ class ScopedSessionTest(fixtures.MappedTest):
mock_scope_func.return_value = 1
s2 = Session(autocommit=True)
assert s2.autocommit == True
+
+ def test_methods_etc(self):
+ mock_session = Mock()
+ mock_session.bind = "the bind"
+
+ sess = scoped_session(lambda: mock_session)
+
+ sess.add("add")
+ sess.delete("delete")
+
+ eq_(sess.bind, "the bind")
+
+ eq_(
+ mock_session.mock_calls,
+ [mock.call.add("add", True), mock.call.delete("delete")],
+ )
+
+ with mock.patch(
+ "sqlalchemy.orm.session.object_session"
+ ) as mock_object_session:
+ sess.object_session("foo")
+
+ eq_(mock_object_session.mock_calls, [mock.call("foo")])
diff --git a/test/orm/test_session.py b/test/orm/test_session.py
index 9bc6c6f7c..4562df44a 100644
--- a/test/orm/test_session.py
+++ b/test/orm/test_session.py
@@ -1,3 +1,5 @@
+import inspect as _py_inspect
+
import sqlalchemy as sa
from sqlalchemy import event
from sqlalchemy import ForeignKey
@@ -1820,22 +1822,28 @@ class DisposedStates(fixtures.MappedTest):
class SessionInterface(fixtures.TestBase):
"""Bogus args to Session methods produce actionable exceptions."""
- # TODO: expand with message body assertions.
-
_class_methods = set(("connection", "execute", "get_bind", "scalar"))
def _public_session_methods(self):
Session = sa.orm.session.Session
- blacklist = set(("begin", "query"))
-
+ blacklist = {"begin", "query", "bind_mapper", "get", "bind_table"}
+ specials = {"__iter__", "__contains__"}
ok = set()
- for meth in Session.public_methods:
- if meth in blacklist:
- continue
- spec = inspect_getfullargspec(getattr(Session, meth))
- if len(spec[0]) > 1 or spec[1]:
- ok.add(meth)
+ for name in dir(Session):
+ if (
+ name in Session.__dict__
+ and (not name.startswith("_") or name in specials)
+ and (
+ _py_inspect.ismethod(getattr(Session, name))
+ or _py_inspect.isfunction(getattr(Session, name))
+ )
+ ):
+ if name in blacklist:
+ continue
+ spec = inspect_getfullargspec(getattr(Session, name))
+ if len(spec[0]) > 1 or spec[1]:
+ ok.add(name)
return ok
def _map_it(self, cls):
@@ -1866,18 +1874,21 @@ class SessionInterface(fixtures.TestBase):
def raises_(method, *args, **kw):
x_raises_(create_session(), method, *args, **kw)
- raises_("__contains__", user_arg)
-
- raises_("add", user_arg)
+ for name in [
+ "__contains__",
+ "is_modified",
+ "merge",
+ "refresh",
+ "add",
+ "delete",
+ "expire",
+ "expunge",
+ "enable_relationship_loading",
+ ]:
+ raises_(name, user_arg)
raises_("add_all", (user_arg,))
- raises_("delete", user_arg)
-
- raises_("expire", user_arg)
-
- raises_("expunge", user_arg)
-
# flush will no-op without something in the unit of work
def _():
class OK(object):
@@ -1891,12 +1902,6 @@ class SessionInterface(fixtures.TestBase):
_()
- raises_("is_modified", user_arg)
-
- raises_("merge", user_arg)
-
- raises_("refresh", user_arg)
-
instance_methods = (
self._public_session_methods()
- self._class_methods