diff options
author | Mike Bayer <mike_mp@zzzcomputing.com> | 2014-03-11 12:27:10 -0400 |
---|---|---|
committer | Mike Bayer <mike_mp@zzzcomputing.com> | 2014-03-11 12:27:10 -0400 |
commit | 009df6a3d041e517cc9efa74d3c87184357a5006 (patch) | |
tree | 1f76d29b586de7052c1baaf286407d4245795788 | |
parent | 043dc4a2c1eef11abc04919d0cc093f5424028e5 (diff) | |
download | sqlalchemy-009df6a3d041e517cc9efa74d3c87184357a5006.tar.gz |
- Added a new keyword argument ``once=True`` to :func:`.event.listen`
and :func:`.event.listens_for`. This is a convenience feature which
will wrap the given listener such that it is only invoked once.
-rw-r--r-- | doc/build/changelog/changelog_09.rst | 9 | ||||
-rw-r--r-- | lib/sqlalchemy/engine/strategies.py | 3 | ||||
-rw-r--r-- | lib/sqlalchemy/event/api.py | 24 | ||||
-rw-r--r-- | lib/sqlalchemy/event/registry.py | 9 | ||||
-rw-r--r-- | lib/sqlalchemy/util/langhelpers.py | 2 | ||||
-rw-r--r-- | test/base/test_events.py | 25 |
6 files changed, 66 insertions, 6 deletions
diff --git a/doc/build/changelog/changelog_09.rst b/doc/build/changelog/changelog_09.rst index 0e5b43b2d..58ac33559 100644 --- a/doc/build/changelog/changelog_09.rst +++ b/doc/build/changelog/changelog_09.rst @@ -15,7 +15,14 @@ :version: 0.9.4 .. change:: - :tags: enhancement, oracle + :tags: feature, orm + + Added a new keyword argument ``once=True`` to :func:`.event.listen` + and :func:`.event.listens_for`. This is a convenience feature which + will wrap the given listener such that it is only invoked once. + + .. change:: + :tags: feature, oracle :tickets: 2911 :pullreq: github:74 diff --git a/lib/sqlalchemy/engine/strategies.py b/lib/sqlalchemy/engine/strategies.py index f6c064033..a8a63bb3d 100644 --- a/lib/sqlalchemy/engine/strategies.py +++ b/lib/sqlalchemy/engine/strategies.py @@ -158,13 +158,12 @@ class DefaultEngineStrategy(EngineStrategy): event.listen(pool, 'first_connect', on_connect) event.listen(pool, 'connect', on_connect) - @util.only_once def first_connect(dbapi_connection, connection_record): c = base.Connection(engine, connection=dbapi_connection, _has_events=False) dialect.initialize(c) - event.listen(pool, 'first_connect', first_connect) + event.listen(pool, 'first_connect', first_connect, once=True) return engine diff --git a/lib/sqlalchemy/event/api.py b/lib/sqlalchemy/event/api.py index 20e74d90e..b27ce7993 100644 --- a/lib/sqlalchemy/event/api.py +++ b/lib/sqlalchemy/event/api.py @@ -44,6 +44,18 @@ def listen(target, identifier, fn, *args, **kw): "after_parent_attach", unique_constraint_name) + + A given function can also be invoked for only the first invocation + of the event using the ``once`` argument:: + + def on_config(): + do_config() + + event.listen(Mapper, "before_configure", on_config, once=True) + + .. versionadded:: 0.9.3 Added ``once=True`` to :func:`.event.listen` + and :func:`.event.listens_for`. + """ _event_key(target, identifier, fn).listen(*args, **kw) @@ -63,6 +75,18 @@ def listens_for(target, identifier, *args, **kw): table.name, list(const.columns)[0].name ) + + A given function can also be invoked for only the first invocation + of the event using the ``once`` argument:: + + @event.listens_for(Mapper, "before_configure", once=True) + def on_config(): + do_config() + + + .. versionadded:: 0.9.3 Added ``once=True`` to :func:`.event.listen` + and :func:`.event.listens_for`. + """ def decorate(fn): listen(target, identifier, fn, *args, **kw) diff --git a/lib/sqlalchemy/event/registry.py b/lib/sqlalchemy/event/registry.py index 7710ff2d2..6f3eb3e85 100644 --- a/lib/sqlalchemy/event/registry.py +++ b/lib/sqlalchemy/event/registry.py @@ -19,7 +19,7 @@ from __future__ import absolute_import import weakref import collections import types -from .. import exc +from .. import exc, util _key_to_collection = collections.defaultdict(dict) @@ -173,7 +173,11 @@ class _EventKey(object): ) def listen(self, *args, **kw): - self.dispatch_target.dispatch._listen(self, *args, **kw) + once = kw.pop("once", False) + if once: + self.with_wrapper(util.only_once(self._listen_fn)).listen(*args, **kw) + else: + self.dispatch_target.dispatch._listen(self, *args, **kw) def remove(self): key = self._key @@ -234,3 +238,4 @@ class _EventKey(object): _stored_in_collection(self, owner) list_.insert(0, self._listen_fn) + diff --git a/lib/sqlalchemy/util/langhelpers.py b/lib/sqlalchemy/util/langhelpers.py index 7b97f8827..8a1164e77 100644 --- a/lib/sqlalchemy/util/langhelpers.py +++ b/lib/sqlalchemy/util/langhelpers.py @@ -1202,7 +1202,7 @@ def only_once(fn): once_fn = once.pop() return once_fn(*arg, **kw) - return update_wrapper(go, fn) + return go _SQLA_RE = re.compile(r'sqlalchemy/([a-z_]+/){0,2}[a-z_]+\.py') diff --git a/test/base/test_events.py b/test/base/test_events.py index 8d4728a9f..4ae89fe17 100644 --- a/test/base/test_events.py +++ b/test/base/test_events.py @@ -1045,6 +1045,31 @@ class RemovalTest(fixtures.TestBase): eq_(f1.mock.mock_calls, [call("x")]) eq_(f2.mock.mock_calls, [call("x"), call("y")]) + def test_once(self): + Target = self._fixture() + + m1 = Mock() + m2 = Mock() + m3 = Mock() + m4 = Mock() + + event.listen(Target, "event_one", m1) + event.listen(Target, "event_one", m2, once=True) + event.listen(Target, "event_one", m3, once=True) + + t1 = Target() + t1.dispatch.event_one("x") + t1.dispatch.event_one("y") + + event.listen(Target, "event_one", m4, once=True) + t1.dispatch.event_one("z") + t1.dispatch.event_one("q") + + eq_(m1.mock_calls, [call("x"), call("y"), call("z"), call("q")]) + eq_(m2.mock_calls, [call("x")]) + eq_(m3.mock_calls, [call("x")]) + eq_(m4.mock_calls, [call("z")]) + def test_propagate(self): Target = self._fixture() |