summaryrefslogtreecommitdiffstats
path: root/Lib/weakref.py
diff options
context:
space:
mode:
authorAntoine Pitrou <solipsis@pitrou.net>2012-11-17 17:57:38 (GMT)
committerAntoine Pitrou <solipsis@pitrou.net>2012-11-17 17:57:38 (GMT)
commitc3afba104aef5032e114b4f5cac0a3bbdfef2bba (patch)
treee4a174e067a9af550dba2b851b647a2f354ce54c /Lib/weakref.py
parent25bbe5e0bce2d8b6c96a5e632bdb9007a0bf0954 (diff)
downloadcpython-c3afba104aef5032e114b4f5cac0a3bbdfef2bba.zip
cpython-c3afba104aef5032e114b4f5cac0a3bbdfef2bba.tar.gz
cpython-c3afba104aef5032e114b4f5cac0a3bbdfef2bba.tar.bz2
Issue #14631: Add a new :class:`weakref.WeakMethod` to simulate weak references to bound methods.
Diffstat (limited to 'Lib/weakref.py')
-rw-r--r--Lib/weakref.py56
1 files changed, 55 insertions, 1 deletions
diff --git a/Lib/weakref.py b/Lib/weakref.py
index 339fcf4..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):