diff options
Diffstat (limited to 'lib/sqlalchemy/event.py')
-rw-r--r-- | lib/sqlalchemy/event.py | 97 |
1 files changed, 70 insertions, 27 deletions
diff --git a/lib/sqlalchemy/event.py b/lib/sqlalchemy/event.py index 1b0b62b34..f844b3345 100644 --- a/lib/sqlalchemy/event.py +++ b/lib/sqlalchemy/event.py @@ -1,44 +1,87 @@ +""" +The event system handles all events throughout the sqlalchemy +and sqlalchemy.orm packages. + +Event specifications: + +:attr:`sqlalchemy.pool.Pool.events` + +""" + from sqlalchemy import util def listen(fn, identifier, target, *args): """Listen for events, passing to fn.""" - target._dispatch.append(fn, identifier, target, *args) + getattr(target.events, identifier).append(fn, target) NO_RESULT = util.symbol('no_result') +class _DispatchMeta(type): + def __init__(cls, classname, bases, dict_): + for k in dict_: + if k.startswith('on_'): + setattr(cls, k, EventDescriptor(dict_[k])) + return type.__init__(cls, classname, bases, dict_) -class Dispatch(object): +class Events(object): + __metaclass__ = _DispatchMeta + + def __init__(self, parent_cls): + self.parent_cls = parent_cls + + +class _ExecEvent(object): + def exec_and_clear(self, *args, **kw): + """Execute the given event once, then clear all listeners.""" - def append(self, identifier, fn, target): - getattr(self, identifier).append(fn) - - def __getattr__(self, key): - self.__dict__[key] = coll = [] - return coll - - def chain(self, identifier, chain_kw, **kw): - ret = NO_RESULT - for fn in getattr(self, identifier): - ret = fn(**kw) - kw['chain_kw'] = ret - return ret - - def __call__(self, identifier, **kw): - for fn in getattr(self, identifier): - fn(**kw) + self(*args, **kw) + self[:] = [] + def __call__(self, *args, **kw): + """Execute the given 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. + + """ + def __init__(self, fn): + self.__name__ = fn.__name__ + self.__doc__ = fn.__doc__ + self._clslevel = [] + + def append(self, obj, target): + self._clslevel.append((obj, target)) + + def __get__(self, obj, cls): + if obj is None: + return self + obj.__dict__[self.__name__] = result = Listeners() + result.extend([ + fn for fn, target in + self._clslevel + if issubclass(obj.parent_cls, target) + ]) + return result + +class Listeners(_ExecEvent, list): + """Represent a collection of listeners linked + to an instance of :class:`Events`.""" + + def append(self, obj, target): + list.append(self, obj) + class dispatcher(object): - def __init__(self, dispatch_cls=Dispatch): - self.dispatch_cls = dispatch_cls - self._dispatch = dispatch_cls() + def __init__(self, events): + self.dispatch_cls = events def __get__(self, obj, cls): if obj is None: - return self._dispatch - obj.__dict__['_dispatch'] = disp = self.dispatch_cls() - for key in self._dispatch.__dict__: - if key.startswith('on_'): - disp.__dict__[key] = self._dispatch.__dict__[k].copy() + return self.dispatch_cls + obj.__dict__['events'] = disp = self.dispatch_cls(cls) return disp |