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
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):
@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__
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
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
@classmethod
def listen(cls, fn, identifier, target, active_history=False):
if active_history:
- target.events.active_history = True
+ target.active_history = True
event.Events.listen(fn, identifier, target)
def on_append(self, state, value, initiator):
set.
"""
-
- events = event.dispatcher(events)
+ dispatch = event.dispatcher(events)
+
def get_history(self, instance, **kwargs):
return self.impl.get_history(instance_state(instance),
"""internal implementation for instrumented attributes."""
def __init__(self, class_, key,
- callable_, events, trackparent=False, extension=None,
+ callable_, dispatch, trackparent=False, extension=None,
compare_function=None, active_history=False,
parent_token=None, expire_missing=True,
**kwargs):
collection attribute when it's first accessed, if not present
already.
- events
- The :class:`Events` object used to track events.
-
trackparent
if True, attempt to track if an instance has a parent attached
to it via this attribute.
self.class_ = class_
self.key = key
self.callable_ = callable_
- self.events = events
+ self.active_history = False
+ self.dispatch = dispatch
self.trackparent = trackparent
self.parent_token = parent_token or self
if compare_function is None:
ext._adapt_listener(attr, ext)
if active_history:
- events.active_history = True
+ self.active_history = True
self.expire_missing = expire_missing
def delete(self, state, dict_):
# TODO: catch key errors, convert to attributeerror?
- if self.events.active_history:
+ if self.active_history:
old = self.get(state, dict_)
else:
old = dict_.get(self.key, NO_VALUE)
- if self.events.on_remove:
+ if self.dispatch.on_remove:
self.fire_remove_event(state, dict_, old, None)
state.modified_event(dict_, self, False, old)
del dict_[self.key]
if initiator is self:
return
- if self.events.active_history:
+ if self.active_history:
old = self.get(state, dict_)
else:
old = dict_.get(self.key, NO_VALUE)
- if self.events.on_set:
+ if self.dispatch.on_set:
value = self.fire_replace_event(state, dict_,
value, old, initiator)
state.modified_event(dict_, self, False, old)
dict_[self.key] = value
def fire_replace_event(self, state, dict_, value, previous, initiator):
- for fn in self.events.on_set:
+ for fn in self.dispatch.on_set:
value = fn(state, value, previous, initiator or self)
return value
def fire_remove_event(self, state, dict_, value, initiator):
- for fn in self.events.on_remove:
+ for fn in self.dispatch.on_remove:
fn(state, value, initiator or self)
@property
uses_objects = False
supports_population = True
- def __init__(self, class_, key, callable_, events,
+ def __init__(self, class_, key, callable_, dispatch,
class_manager, copy_function=None,
compare_function=None, **kwargs):
super(ScalarAttributeImpl, self).__init__(
class_,
key,
- callable_, events,
+ callable_, dispatch,
compare_function=compare_function,
**kwargs)
class_manager.mutable_attributes.add(key)
if initiator is self:
return
- if self.events.on_set:
+ if self.dispatch.on_set:
old = self.get(state, dict_)
value = self.fire_replace_event(state, dict_,
value, old, initiator)
uses_objects = True
supports_population = True
- def __init__(self, class_, key, callable_, events,
+ def __init__(self, class_, key, callable_, dispatch,
trackparent=False, extension=None, copy_function=None,
compare_function=None, **kwargs):
super(ScalarObjectAttributeImpl, self).__init__(
class_,
key,
- callable_, events,
+ callable_, dispatch,
trackparent=trackparent,
extension=extension,
compare_function=compare_function,
if initiator is self:
return
- if self.events.active_history:
+ if self.active_history:
old = self.get(state, dict_)
else:
old = self.get(state, dict_, passive=PASSIVE_NO_FETCH)
if self.trackparent and value is not None:
self.sethasparent(instance_state(value), False)
- for fn in self.events.on_remove:
+ for fn in self.dispatch.on_remove:
fn(state, value, initiator or self)
state.modified_event(dict_, self, False, value)
previous is not PASSIVE_NO_RESULT):
self.sethasparent(instance_state(previous), False)
- for fn in self.events.on_set:
+ for fn in self.dispatch.on_set:
value = fn(state, value, previous, initiator or self)
state.modified_event(dict_, self, False, previous)
uses_objects = True
supports_population = True
- def __init__(self, class_, key, callable_, events,
+ def __init__(self, class_, key, callable_, dispatch,
typecallable=None, trackparent=False, extension=None,
copy_function=None, compare_function=None, **kwargs):
super(CollectionAttributeImpl, self).__init__(
class_,
key,
- callable_, events,
+ callable_, dispatch,
trackparent=trackparent,
extension=extension,
compare_function=compare_function,
return History.from_attribute(self, state, current)
def fire_append_event(self, state, dict_, value, initiator):
- for fn in self.events.on_append:
+ for fn in self.dispatch.on_append:
value = fn(state, value, initiator or self)
state.modified_event(dict_, self, True,
if self.trackparent and value is not None:
self.sethasparent(instance_state(value), False)
- for fn in self.events.on_remove:
+ for fn in self.dispatch.on_remove:
fn(state, value, initiator or self)
state.modified_event(dict_, self, True,
def on_resurrect(self, state, instance):
""""""
- events = event.dispatcher(events)
+ dispatch = event.dispatcher(events)
@property
def is_mapped(self):
self._uninstrument_init()
- self.mapper = self.events = None
+ self.mapper = self.dispatch = None
self.info.clear()
for key in list(self):
else:
typecallable = kw.pop('typecallable', None)
- events = manager[key].events
+ dispatch = manager[key].dispatch
if impl_class:
impl = impl_class(class_, key, typecallable, **kw)
elif uselist:
- impl = CollectionAttributeImpl(class_, key, callable_, events,
+ impl = CollectionAttributeImpl(class_, key, callable_, dispatch,
typecallable=typecallable, **kw)
elif useobject:
- impl = ScalarObjectAttributeImpl(class_, key, callable_, events,**kw)
+ impl = ScalarObjectAttributeImpl(class_, key, callable_, dispatch,**kw)
elif mutable_scalars:
- impl = MutableScalarAttributeImpl(class_, key, callable_, events,
+ impl = MutableScalarAttributeImpl(class_, key, callable_, dispatch,
class_manager=manager, **kw)
else:
- impl = ScalarAttributeImpl(class_, key, callable_, events, **kw)
+ impl = ScalarAttributeImpl(class_, key, callable_, dispatch, **kw)
manager[key].impl = impl