summaryrefslogtreecommitdiff
path: root/lib/sqlalchemy/event/attr.py
diff options
context:
space:
mode:
authorMike Bayer <mike_mp@zzzcomputing.com>2015-01-04 15:07:36 -0500
committerMike Bayer <mike_mp@zzzcomputing.com>2015-01-04 18:06:02 -0500
commitcf272325635c1205da7fd2668eac3c8ac409dafb (patch)
tree7a34220d9c63d54bf2b072786062851fd9a137c3 /lib/sqlalchemy/event/attr.py
parent315db703a63f5fe5fecf6417f78ff513ff091966 (diff)
downloadsqlalchemy-cf272325635c1205da7fd2668eac3c8ac409dafb.tar.gz
- wip - start factoring events so that we aren't using descriptors for dispatch,
allowing us to move to __slots__
Diffstat (limited to 'lib/sqlalchemy/event/attr.py')
-rw-r--r--lib/sqlalchemy/event/attr.py75
1 files changed, 28 insertions, 47 deletions
diff --git a/lib/sqlalchemy/event/attr.py b/lib/sqlalchemy/event/attr.py
index 5e3499209..de5d34950 100644
--- a/lib/sqlalchemy/event/attr.py
+++ b/lib/sqlalchemy/event/attr.py
@@ -46,11 +46,11 @@ class RefCollection(object):
return weakref.ref(self, registry._collection_gced)
-class _DispatchDescriptor(RefCollection):
- """Class-level attributes on :class:`._Dispatch` classes."""
+class _ClsLevelDispatch(RefCollection):
+ """Class-level events on :class:`._Dispatch` classes."""
def __init__(self, parent_dispatch_cls, fn):
- self.__name__ = fn.__name__
+ self.name = fn.__name__
argspec = util.inspect_getargspec(fn)
self.arg_names = argspec.args[1:]
self.has_kw = bool(argspec.keywords)
@@ -64,7 +64,6 @@ class _DispatchDescriptor(RefCollection):
self, parent_dispatch_cls, fn)
self._clslevel = weakref.WeakKeyDictionary()
- self._empty_listeners = weakref.WeakKeyDictionary()
def _adjust_fn_spec(self, fn, named):
if named:
@@ -152,34 +151,23 @@ class _DispatchDescriptor(RefCollection):
def for_modify(self, obj):
"""Return an event collection which can be modified.
- For _DispatchDescriptor at the class level of
+ For _ClsLevelDispatch at the class level of
a dispatcher, this returns self.
"""
return self
- def __get__(self, obj, cls):
- if obj is None:
- return self
- elif obj._parent_cls in self._empty_listeners:
- ret = self._empty_listeners[obj._parent_cls]
- else:
- self._empty_listeners[obj._parent_cls] = ret = \
- _EmptyListener(self, obj._parent_cls)
- # assigning it to __dict__ means
- # memoized for fast re-access. but more memory.
- obj.__dict__[self.__name__] = ret
- return ret
+class _InstanceLevelDispatch(object):
+ __slots__ = ()
-class _HasParentDispatchDescriptor(object):
def _adjust_fn_spec(self, fn, named):
return self.parent._adjust_fn_spec(fn, named)
-class _EmptyListener(_HasParentDispatchDescriptor):
- """Serves as a class-level interface to the events
- served by a _DispatchDescriptor, when there are no
+class _EmptyListener(_InstanceLevelDispatch):
+ """Serves as a proxy interface to the events
+ served by a _ClsLevelDispatch, when there are no
instance-level events present.
Is replaced by _ListenerCollection when instance-level
@@ -187,14 +175,17 @@ class _EmptyListener(_HasParentDispatchDescriptor):
"""
+ propagate = frozenset()
+ listeners = ()
+
+ __slots__ = 'parent', 'parent_listeners', 'name'
+
def __init__(self, parent, target_cls):
if target_cls not in parent._clslevel:
parent.update_subclass(target_cls)
- self.parent = parent # _DispatchDescriptor
+ self.parent = parent # _ClsLevelDispatch
self.parent_listeners = parent._clslevel[target_cls]
- self.name = parent.__name__
- self.propagate = frozenset()
- self.listeners = ()
+ self.name = parent.name
def for_modify(self, obj):
"""Return an event collection which can be modified.
@@ -205,9 +196,11 @@ class _EmptyListener(_HasParentDispatchDescriptor):
and returns it.
"""
- result = _ListenerCollection(self.parent, obj._parent_cls)
- if obj.__dict__[self.name] is self:
- obj.__dict__[self.name] = result
+ result = _ListenerCollection(self.parent, obj._instance_cls)
+ if getattr(obj, self.name) is self:
+ setattr(obj, self.name, result)
+ else:
+ assert isinstance(getattr(obj, self.name), _JoinedListener)
return result
def _needs_modify(self, *args, **kw):
@@ -233,9 +226,11 @@ class _EmptyListener(_HasParentDispatchDescriptor):
__nonzero__ = __bool__
-class _CompoundListener(_HasParentDispatchDescriptor):
+class _CompoundListener(_InstanceLevelDispatch):
_exec_once = False
+ __slots__ = ()
+
@util.memoized_property
def _exec_once_mutex(self):
return threading.Lock()
@@ -282,12 +277,15 @@ class _ListenerCollection(RefCollection, _CompoundListener):
"""
+ # RefCollection has a @memoized_property, so can't do
+ # __slots__ here
+
def __init__(self, parent, target_cls):
if target_cls not in parent._clslevel:
parent.update_subclass(target_cls)
self.parent_listeners = parent._clslevel[target_cls]
self.parent = parent
- self.name = parent.__name__
+ self.name = parent.name
self.listeners = collections.deque()
self.propagate = set()
@@ -339,23 +337,6 @@ class _ListenerCollection(RefCollection, _CompoundListener):
self.listeners.clear()
-class _JoinedDispatchDescriptor(object):
- __slots__ = 'name',
-
- def __init__(self, name):
- self.name = name
-
- def __get__(self, obj, cls):
- if obj is None:
- return self
- else:
- obj.__dict__[self.name] = ret = _JoinedListener(
- obj.parent, self.name,
- getattr(obj.local, self.name)
- )
- return ret
-
-
class _JoinedListener(_CompoundListener):
_exec_once = False