summaryrefslogtreecommitdiff
path: root/lib/sqlalchemy/event.py
diff options
context:
space:
mode:
authorMike Bayer <mike_mp@zzzcomputing.com>2010-08-10 00:19:50 -0400
committerMike Bayer <mike_mp@zzzcomputing.com>2010-08-10 00:19:50 -0400
commit87293d5b961afd5e1966f3c533923ac0ecd2a781 (patch)
treee265e39ae9c5fa3b4f855d77a058bc9f9f87abca /lib/sqlalchemy/event.py
parentf665ae746428cdb69e97d4576da29268a388569a (diff)
downloadsqlalchemy-87293d5b961afd5e1966f3c533923ac0ecd2a781.tar.gz
- reorganize events into cls->dispatch, which is an instance of _Dispatch.
cut down on extraneous stuff, cleanup The Event class never gets instantiated and its methods stay the same so that sphinx can get to it.
Diffstat (limited to 'lib/sqlalchemy/event.py')
-rw-r--r--lib/sqlalchemy/event.py135
1 files changed, 79 insertions, 56 deletions
diff --git a/lib/sqlalchemy/event.py b/lib/sqlalchemy/event.py
index bfa617a25..21f05a1c9 100644
--- a/lib/sqlalchemy/event.py
+++ b/lib/sqlalchemy/event.py
@@ -16,26 +16,17 @@ def listen(fn, identifier, target, *args, **kw):
for evt_cls in _registrars[identifier]:
for tgt in evt_cls.accept_with(target):
-
- tgt.dispatch.events.listen(fn, identifier, tgt, *args, **kw)
+ tgt.dispatch.listen(fn, identifier, tgt, *args, **kw)
break
-class _DispatchMeta(type):
- def __init__(cls, classname, bases, dict_):
-
- dispatch_base = getattr(cls, 'dispatch', Dispatch)
- cls.dispatch = dispatch_cls = type("%sDispatch" % classname, (dispatch_base, ), {})
- dispatch_cls.events = cls
- for k in dict_:
- if k.startswith('on_'):
- setattr(dispatch_cls, k, EventDescriptor(dict_[k]))
- _registrars[k].append(cls)
- return type.__init__(cls, classname, bases, dict_)
-
_registrars = util.defaultdict(list)
-class Dispatch(object):
-
+class _Dispatch(object):
+ """Mirror the event listening definitions of an Events class with
+ listener collections.
+
+ """
+
def __init__(self, parent_cls):
self.parent_cls = parent_cls
@@ -49,9 +40,33 @@ class Dispatch(object):
for ls in other.descriptors:
getattr(self, ls.name).listeners.extend(ls.listeners)
+class _EventMeta(type):
+ """Intercept new Event subclasses and create
+ associated _Dispatch classes."""
+
+ def __init__(cls, classname, bases, dict_):
+ _create_dispatcher_class(cls, classname, bases, dict_)
+ return type.__init__(cls, classname, bases, dict_)
+
+def _create_dispatcher_class(cls, classname, bases, dict_):
+ # there's all kinds of ways to do this,
+ # i.e. make a Dispatch class that shares the 'listen' method
+ # of the Event class, this is the straight monkeypatch.
+ dispatch_base = getattr(cls, 'dispatch', _Dispatch)
+ cls.dispatch = dispatch_cls = type("%sDispatch" % classname,
+ (dispatch_base, ), {})
+ dispatch_cls.listen = cls.listen
+
+ for k in dict_:
+ if k.startswith('on_'):
+ setattr(dispatch_cls, k, _DispatchDescriptor(dict_[k]))
+ _registrars[k].append(cls)
class Events(object):
- __metaclass__ = _DispatchMeta
+ """Define event listening functions for a particular target type."""
+
+
+ __metaclass__ = _EventMeta
@classmethod
def accept_with(cls, target):
@@ -69,46 +84,12 @@ class Events(object):
@classmethod
def listen(cls, fn, identifier, target):
getattr(target.dispatch, identifier).append(fn, target)
-
-class _ExecEvent(object):
- _exec_once = False
-
- def exec_once(self, *args, **kw):
- """Execute this event, but only if it has not been
- executed already for this collection."""
-
- if not self._exec_once:
- self(*args, **kw)
- self._exec_once = True
-
- def exec_until_return(self, *args, **kw):
- """Execute listeners for this event until
- one returns a non-None value.
-
- Returns the value, or None.
- """
-
- if self:
- for fn in self:
- r = fn(*args, **kw)
- if r is not None:
- return r
- return None
-
- def __call__(self, *args, **kw):
- """Execute this event."""
-
- if self:
- for fn in self:
- fn(*args, **kw)
-class EventDescriptor(object):
- """Represent an event type associated with a :class:`Events` class
- as well as class-level listeners.
+class _DispatchDescriptor(object):
+ """Class-level attributes on _Dispatch classes."""
- """
def __init__(self, fn):
self.__name__ = fn.__name__
self.__doc__ = fn.__doc__
@@ -117,23 +98,58 @@ class EventDescriptor(object):
def append(self, obj, target):
assert isinstance(target, type), \
"Class-level Event targets must be classes."
+
for cls in [target] + target.__subclasses__():
self._clslevel[cls].append(obj)
def __get__(self, obj, cls):
if obj is None:
return self
- obj.__dict__[self.__name__] = result = Listeners(self, obj.parent_cls)
+ obj.__dict__[self.__name__] = result = \
+ _ListenerCollection(self, obj.parent_cls)
return result
-class Listeners(_ExecEvent):
+class _ListenerCollection(object):
"""Represent a collection of listeners linked
- to an instance of :class:`Events`."""
+ to an instance of _Dispatch.
+
+ """
+
+ _exec_once = False
def __init__(self, parent, target_cls):
self.parent_listeners = parent._clslevel[target_cls]
self.name = parent.__name__
self.listeners = []
+
+ def exec_once(self, *args, **kw):
+ """Execute this event, but only if it has not been
+ executed already for this collection."""
+
+ if not self._exec_once:
+ self(*args, **kw)
+ self._exec_once = True
+
+ def exec_until_return(self, *args, **kw):
+ """Execute listeners for this event until
+ one returns a non-None value.
+
+ Returns the value, or None.
+ """
+
+ if self:
+ for fn in self:
+ r = fn(*args, **kw)
+ if r is not None:
+ return r
+ return None
+
+ def __call__(self, *args, **kw):
+ """Execute this event."""
+
+ if self:
+ for fn in self:
+ fn(*args, **kw)
# I'm not entirely thrilled about the overhead here,
# but this allows class-level listeners to be added
@@ -156,9 +172,16 @@ class Listeners(_ExecEvent):
self.listeners.append(obj)
class dispatcher(object):
+ """Descriptor used by target classes to
+ deliver the _Dispatch class at the class level
+ and produce new _Dispatch instances for target
+ instances.
+
+ """
def __init__(self, events):
self.dispatch_cls = events.dispatch
+
def __get__(self, obj, cls):
if obj is None:
return self.dispatch_cls