From: Mike Bayer Date: Tue, 10 Aug 2010 04:19:50 +0000 (-0400) Subject: - reorganize events into cls->dispatch, which is an instance of _Dispatch. X-Git-Tag: rel_0_7b1~253^2~33 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=87293d5b961afd5e1966f3c533923ac0ecd2a781;p=thirdparty%2Fsqlalchemy%2Fsqlalchemy.git - 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. --- diff --git a/lib/sqlalchemy/event.py b/lib/sqlalchemy/event.py index bfa617a25b..21f05a1c95 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 diff --git a/lib/sqlalchemy/orm/attributes.py b/lib/sqlalchemy/orm/attributes.py index 9d454a7b28..729968940b 100644 --- a/lib/sqlalchemy/orm/attributes.py +++ b/lib/sqlalchemy/orm/attributes.py @@ -129,7 +129,7 @@ class QueryableAttribute(interfaces.PropComparator): @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): @@ -154,8 +154,8 @@ class QueryableAttribute(interfaces.PropComparator): set. """ - - events = event.dispatcher(events) + dispatch = event.dispatcher(events) + def get_history(self, instance, **kwargs): return self.impl.get_history(instance_state(instance), @@ -289,7 +289,7 @@ class AttributeImpl(object): """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): @@ -307,9 +307,6 @@ class AttributeImpl(object): 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. @@ -342,7 +339,8 @@ class AttributeImpl(object): 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: @@ -358,7 +356,7 @@ class AttributeImpl(object): ext._adapt_listener(attr, ext) if active_history: - events.active_history = True + self.active_history = True self.expire_missing = expire_missing @@ -495,12 +493,12 @@ class ScalarAttributeImpl(AttributeImpl): 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] @@ -513,24 +511,24 @@ class ScalarAttributeImpl(AttributeImpl): 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 @@ -547,13 +545,13 @@ class MutableScalarAttributeImpl(ScalarAttributeImpl): 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) @@ -592,7 +590,7 @@ class MutableScalarAttributeImpl(ScalarAttributeImpl): 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) @@ -614,13 +612,13 @@ class ScalarObjectAttributeImpl(ScalarAttributeImpl): 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, @@ -654,7 +652,7 @@ class ScalarObjectAttributeImpl(ScalarAttributeImpl): 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) @@ -666,7 +664,7 @@ class ScalarObjectAttributeImpl(ScalarAttributeImpl): 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) @@ -678,7 +676,7 @@ class ScalarObjectAttributeImpl(ScalarAttributeImpl): 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) @@ -705,13 +703,13 @@ class CollectionAttributeImpl(AttributeImpl): 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, @@ -733,7 +731,7 @@ class CollectionAttributeImpl(AttributeImpl): 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, @@ -752,7 +750,7 @@ class CollectionAttributeImpl(AttributeImpl): 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, @@ -1006,7 +1004,7 @@ class ClassManager(dict): def on_resurrect(self, state, instance): """""" - events = event.dispatcher(events) + dispatch = event.dispatcher(events) @property def is_mapped(self): @@ -1132,7 +1130,7 @@ class ClassManager(dict): self._uninstrument_init() - self.mapper = self.events = None + self.mapper = self.dispatch = None self.info.clear() for key in list(self): @@ -1499,20 +1497,20 @@ def register_attribute_impl(class_, key, 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 diff --git a/lib/sqlalchemy/orm/state.py b/lib/sqlalchemy/orm/state.py index 159f60e717..a02f2dafdb 100644 --- a/lib/sqlalchemy/orm/state.py +++ b/lib/sqlalchemy/orm/state.py @@ -89,7 +89,7 @@ class InstanceState(object): self, instance, args = mixed[0], mixed[1], mixed[2:] manager = self.manager - manager.events.on_init(self, instance, args, kwargs) + manager.dispatch.on_init(self, instance, args, kwargs) # LESSTHANIDEAL: # adjust for the case where the InstanceState was created before @@ -102,7 +102,7 @@ class InstanceState(object): try: return manager.original_init(*mixed[1:], **kwargs) except: - manager.events.on_init_failure(self, instance, args, kwargs) + manager.dispatch.on_init_failure(self, instance, args, kwargs) raise def get_history(self, key, **kwargs): @@ -136,7 +136,7 @@ class InstanceState(object): return [x] def _run_on_load(self, instance): - self.manager.events.on_load(instance) + self.manager.dispatch.on_load(instance) def __getstate__(self): d = {'instance':self.obj()} @@ -501,7 +501,7 @@ class MutableAttrInstanceState(InstanceState): obj.__dict__.update(self.mutable_dict) # re-establishes identity attributes from the key - self.manager.events.on_resurrect(self, obj) + self.manager.dispatch.on_resurrect(self, obj) # TODO: don't really think we should run this here. # resurrect is only meant to preserve the minimal state needed to diff --git a/test/engine/test_pool.py b/test/engine/test_pool.py index e09b7a04fe..29591f11e5 100644 --- a/test/engine/test_pool.py +++ b/test/engine/test_pool.py @@ -261,10 +261,10 @@ class PoolTest(PoolTestBase): def assert_listeners(p, total, conn, fconn, cout, cin): for instance in (p, p.recreate()): - self.assert_(len(instance.events.on_connect) == conn) - self.assert_(len(instance.events.on_first_connect) == fconn) - self.assert_(len(instance.events.on_checkout) == cout) - self.assert_(len(instance.events.on_checkin) == cin) + self.assert_(len(instance.dispatch.on_connect) == conn) + self.assert_(len(instance.dispatch.on_first_connect) == fconn) + self.assert_(len(instance.dispatch.on_checkout) == cout) + self.assert_(len(instance.dispatch.on_checkin) == cin) p = _pool() assert_listeners(p, 0, 0, 0, 0, 0) @@ -392,9 +392,9 @@ class PoolTest(PoolTestBase): def assert_listeners(p, total, conn, cout, cin): for instance in (p, p.recreate()): - self.assert_(len(instance.events.on_connect) == conn) - self.assert_(len(instance.events.on_checkout) == cout) - self.assert_(len(instance.events.on_checkin) == cin) + self.assert_(len(instance.dispatch.on_connect) == conn) + self.assert_(len(instance.dispatch.on_checkout) == cout) + self.assert_(len(instance.dispatch.on_checkin) == cin) p = _pool() assert_listeners(p, 0, 0, 0, 0)