diff options
author | Antoine Pitrou <solipsis@pitrou.net> | 2012-11-17 17:57:38 (GMT) |
---|---|---|
committer | Antoine Pitrou <solipsis@pitrou.net> | 2012-11-17 17:57:38 (GMT) |
commit | c3afba104aef5032e114b4f5cac0a3bbdfef2bba (patch) | |
tree | e4a174e067a9af550dba2b851b647a2f354ce54c /Lib/weakref.py | |
parent | 25bbe5e0bce2d8b6c96a5e632bdb9007a0bf0954 (diff) | |
download | cpython-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.py | 56 |
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): |