summaryrefslogtreecommitdiff
path: root/lib/sqlalchemy/event/api.py
diff options
context:
space:
mode:
authorMike Bayer <mike_mp@zzzcomputing.com>2013-07-26 14:21:58 -0400
committerMike Bayer <mike_mp@zzzcomputing.com>2013-07-26 14:21:58 -0400
commit4505425a38b079a8e2a59fdbe31bc033de25e871 (patch)
treed0dd695935aee0e26f8cd9ca36bc39f6df7e66ef /lib/sqlalchemy/event/api.py
parent550141b14c8e165218cd32c27d91541eeee86d2a (diff)
downloadsqlalchemy-4505425a38b079a8e2a59fdbe31bc033de25e871.tar.gz
- 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.
Diffstat (limited to 'lib/sqlalchemy/event/api.py')
-rw-r--r--lib/sqlalchemy/event/api.py99
1 files changed, 99 insertions, 0 deletions
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))
+
+