summaryrefslogtreecommitdiffstats
path: root/Lib/weakref.py
diff options
context:
space:
mode:
Diffstat (limited to 'Lib/weakref.py')
-rw-r--r--Lib/weakref.py59
1 files changed, 56 insertions, 3 deletions
diff --git a/Lib/weakref.py b/Lib/weakref.py
index fcb6b74..8f9c107 100644
--- a/Lib/weakref.py
+++ b/Lib/weakref.py
@@ -27,7 +27,61 @@ ProxyTypes = (ProxyType, CallableProxyType)
__all__ = ["ref", "proxy", "getweakrefcount", "getweakrefs",
"WeakKeyDictionary", "ReferenceType", "ProxyType",
"CallableProxyType", "ProxyTypes", "WeakValueDictionary",
- "WeakSet"]
+ "WeakSet", "WeakMethod"]
+
+
+class WeakMethod(ref):
+ """
+ A custom `weakref.ref` subclass which simulates a weak reference to
+ a bound method, working around the lifetime problem of bound methods.
+ """
+
+ __slots__ = "_func_ref", "_meth_type", "_alive", "__weakref__"
+
+ def __new__(cls, meth, callback=None):
+ try:
+ obj = meth.__self__
+ func = meth.__func__
+ except AttributeError:
+ raise TypeError("argument should be a bound method, not {}"
+ .format(type(meth))) from None
+ def _cb(arg):
+ # The self-weakref trick is needed to avoid creating a reference
+ # cycle.
+ self = self_wr()
+ if self._alive:
+ self._alive = False
+ if callback is not None:
+ callback(self)
+ self = ref.__new__(cls, obj, _cb)
+ self._func_ref = ref(func, _cb)
+ self._meth_type = type(meth)
+ self._alive = True
+ self_wr = ref(self)
+ return self
+
+ def __call__(self):
+ obj = super().__call__()
+ func = self._func_ref()
+ if obj is None or func is None:
+ return None
+ return self._meth_type(func, obj)
+
+ def __eq__(self, other):
+ if isinstance(other, WeakMethod):
+ if not self._alive or not other._alive:
+ return self is other
+ return ref.__eq__(self, other) and self._func_ref == other._func_ref
+ return False
+
+ def __ne__(self, other):
+ if isinstance(other, WeakMethod):
+ if not self._alive or not other._alive:
+ return self is not other
+ return ref.__ne__(self, other) or self._func_ref != other._func_ref
+ return True
+
+ __hash__ = ref.__hash__
class WeakValueDictionary(collections.MutableMapping):
@@ -153,8 +207,7 @@ class WeakValueDictionary(collections.MutableMapping):
"""
with _IterationGuard(self):
- for wr in self.data.values():
- yield wr
+ yield from self.data.values()
def values(self):
with _IterationGuard(self):