From 4505425a38b079a8e2a59fdbe31bc033de25e871 Mon Sep 17 00:00:00 2001 From: Mike Bayer Date: Fri, 26 Jul 2013 14:21:58 -0400 Subject: - Removal of event listeners is now implemented. The feature is provided via the :func:`.event.remove` function. [ticket:2268] - reorganization of event.py module into a package; with the addition of the docstring work as well as the new registry for removal, there's a lot more code now. the package separates concerns and provides a top-level doc for each subsection of functionality - the remove feature works by providing the EventKey object which associates the user-provided arguments to listen() with a global, weak-referencing registry. This registry stores a collection of _ListenerCollection and _DispatchDescriptor objects associated with each set of arguments, as well as the wrapped function which was applied to that collection. The EventKey can then be recreated for a removal, all the _ListenerCollection and _DispatchDescriptor objects are located, and the correct wrapped function is removed from each one. --- lib/sqlalchemy/event/api.py | 99 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 99 insertions(+) create mode 100644 lib/sqlalchemy/event/api.py (limited to 'lib/sqlalchemy/event/api.py') diff --git a/lib/sqlalchemy/event/api.py b/lib/sqlalchemy/event/api.py new file mode 100644 index 000000000..3a6c46e6a --- /dev/null +++ b/lib/sqlalchemy/event/api.py @@ -0,0 +1,99 @@ +"""Public API functions for the event system. + +""" +from __future__ import absolute_import + +from .. import util, exc +from .base import _registrars +from .registry import _EventKey + +CANCEL = util.symbol('CANCEL') +NO_RETVAL = util.symbol('NO_RETVAL') + + +def listen(target, identifier, fn, *args, **kw): + """Register a listener function for the given target. + + e.g.:: + + from sqlalchemy import event + from sqlalchemy.schema import UniqueConstraint + + def unique_constraint_name(const, table): + const.name = "uq_%s_%s" % ( + table.name, + list(const.columns)[0].name + ) + event.listen( + UniqueConstraint, + "after_parent_attach", + unique_constraint_name) + + """ + + for evt_cls in _registrars[identifier]: + tgt = evt_cls._accept_with(target) + if tgt is not None: + _EventKey(target, identifier, fn, tgt).listen(*args, **kw) + break + else: + raise exc.InvalidRequestError("No such event '%s' for target '%s'" % + (identifier, target)) + + +def listens_for(target, identifier, *args, **kw): + """Decorate a function as a listener for the given target + identifier. + + e.g.:: + + from sqlalchemy import event + from sqlalchemy.schema import UniqueConstraint + + @event.listens_for(UniqueConstraint, "after_parent_attach") + def unique_constraint_name(const, table): + const.name = "uq_%s_%s" % ( + table.name, + list(const.columns)[0].name + ) + """ + def decorate(fn): + listen(target, identifier, fn, *args, **kw) + return fn + return decorate + + +def remove(target, identifier, fn): + """Remove an event listener. + + The arguments here should match exactly those which were sent to + :func:`.listen`; all the event registration which proceeded as a result + of this call will be reverted by calling :func:`.remove` with the same + arguments. + + e.g.:: + + # if a function was registered like this... + @event.listens_for(SomeMappedClass, "before_insert", propagate=True) + def my_listener_function(*arg): + pass + + # ... it's removed like this + event.remove(SomeMappedClass, "before_insert", my_listener_function) + + Above, the listener function associated with ``SomeMappedClass`` was also + propagated to subclasses of ``SomeMappedClass``; the :func:`.remove` function + will revert all of these operations. + + .. versionadded:: 0.9.0 + + """ + for evt_cls in _registrars[identifier]: + tgt = evt_cls._accept_with(target) + if tgt is not None: + _EventKey(target, identifier, fn, tgt).remove() + break + else: + raise exc.InvalidRequestError("No such event '%s' for target '%s'" % + (identifier, target)) + + -- cgit v1.2.1 From c903f071a82ce8784a4aa7f5e0d4264443e1944b Mon Sep 17 00:00:00 2001 From: Mike Bayer Date: Fri, 26 Jul 2013 19:11:33 -0400 Subject: - add event.contains() function to the event package, returns True if the given target/event/fn is set up to listen. - repair mutable package which is doing some conditional event listening --- lib/sqlalchemy/event/api.py | 34 ++++++++++++++++++---------------- 1 file changed, 18 insertions(+), 16 deletions(-) (limited to 'lib/sqlalchemy/event/api.py') diff --git a/lib/sqlalchemy/event/api.py b/lib/sqlalchemy/event/api.py index 3a6c46e6a..33a6b817f 100644 --- a/lib/sqlalchemy/event/api.py +++ b/lib/sqlalchemy/event/api.py @@ -11,6 +11,15 @@ CANCEL = util.symbol('CANCEL') NO_RETVAL = util.symbol('NO_RETVAL') +def _event_key(target, identifier, fn): + for evt_cls in _registrars[identifier]: + tgt = evt_cls._accept_with(target) + if tgt is not None: + return _EventKey(target, identifier, fn, tgt) + else: + raise exc.InvalidRequestError("No such event '%s' for target '%s'" % + (identifier, target)) + def listen(target, identifier, fn, *args, **kw): """Register a listener function for the given target. @@ -31,14 +40,7 @@ def listen(target, identifier, fn, *args, **kw): """ - for evt_cls in _registrars[identifier]: - tgt = evt_cls._accept_with(target) - if tgt is not None: - _EventKey(target, identifier, fn, tgt).listen(*args, **kw) - break - else: - raise exc.InvalidRequestError("No such event '%s' for target '%s'" % - (identifier, target)) + _event_key(target, identifier, fn).listen(*args, **kw) def listens_for(target, identifier, *args, **kw): @@ -87,13 +89,13 @@ def remove(target, identifier, fn): .. versionadded:: 0.9.0 """ - for evt_cls in _registrars[identifier]: - tgt = evt_cls._accept_with(target) - if tgt is not None: - _EventKey(target, identifier, fn, tgt).remove() - break - else: - raise exc.InvalidRequestError("No such event '%s' for target '%s'" % - (identifier, target)) + _event_key(target, identifier, fn).remove() + +def contains(target, identifier, fn): + """Return True if the given target/ident/fn is set up to listen. + .. versionadded:: 0.9.0 + + """ + return _event_key(target, identifier, fn).contains() -- cgit v1.2.1 From 42f2a16be7ff462048857799decc41a95c459fc3 Mon Sep 17 00:00:00 2001 From: Mike Bayer Date: Sat, 26 Oct 2013 16:32:17 -0400 Subject: - add copyright to source files missing it --- lib/sqlalchemy/event/api.py | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'lib/sqlalchemy/event/api.py') diff --git a/lib/sqlalchemy/event/api.py b/lib/sqlalchemy/event/api.py index 33a6b817f..5d10bb50b 100644 --- a/lib/sqlalchemy/event/api.py +++ b/lib/sqlalchemy/event/api.py @@ -1,3 +1,9 @@ +# event/api.py +# Copyright (C) 2005-2013 the SQLAlchemy authors and contributors +# +# This module is part of SQLAlchemy and is released under +# the MIT License: http://www.opensource.org/licenses/mit-license.php + """Public API functions for the event system. """ -- cgit v1.2.1 From f89d4d216bd7605c920b7b8a10ecde6bfea2238c Mon Sep 17 00:00:00 2001 From: Mike Bayer Date: Sun, 5 Jan 2014 16:57:05 -0500 Subject: - happy new year --- lib/sqlalchemy/event/api.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib/sqlalchemy/event/api.py') diff --git a/lib/sqlalchemy/event/api.py b/lib/sqlalchemy/event/api.py index 5d10bb50b..20e74d90e 100644 --- a/lib/sqlalchemy/event/api.py +++ b/lib/sqlalchemy/event/api.py @@ -1,5 +1,5 @@ # event/api.py -# Copyright (C) 2005-2013 the SQLAlchemy authors and contributors +# Copyright (C) 2005-2014 the SQLAlchemy authors and contributors # # This module is part of SQLAlchemy and is released under # the MIT License: http://www.opensource.org/licenses/mit-license.php -- cgit v1.2.1