summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--blinker/_saferef.py44
-rw-r--r--blinker/_utilities.py2
-rw-r--r--tests/test_saferef.py5
3 files changed, 32 insertions, 19 deletions
diff --git a/blinker/_saferef.py b/blinker/_saferef.py
index 622ed99..269e362 100644
--- a/blinker/_saferef.py
+++ b/blinker/_saferef.py
@@ -34,9 +34,10 @@
#
"""Refactored 'safe reference from dispatcher.py"""
-import weakref
+import operator
import sys
import traceback
+import weakref
try:
@@ -46,6 +47,14 @@ except NameError:
return hasattr(object, '__call__')
+if sys.version_info < (3,):
+ get_self = operator.attrgetter('im_self')
+ get_func = operator.attrgetter('im_func')
+else:
+ get_self = operator.attrgetter('__self__')
+ get_func = operator.attrgetter('__func__')
+
+
def safe_ref(target, on_delete=None):
"""Return a *safe* weak reference to a callable target.
@@ -58,21 +67,22 @@ def safe_ref(target, on_delete=None):
scope with the reference object, (either a weakref or a
BoundMethodWeakref) as argument.
"""
- if hasattr(target, 'im_self'):
- if target.im_self is not None:
+ try:
+ im_self = get_self(target)
+ except AttributeError:
+ if callable(on_delete):
+ return weakref.ref(target, on_delete)
+ else:
+ return weakref.ref(target)
+ else:
+ if im_self is not None:
# Turn a bound method into a BoundMethodWeakref instance.
# Keep track of these instances for lookup by disconnect().
- assert hasattr(target, 'im_func'), (
+ assert hasattr(target, 'im_func') or hasattr(target, '__func__'), (
"safe_ref target %r has im_self, but no im_func, "
- "don't know how to create reference"
- % target
- )
+ "don't know how to create reference" % target)
reference = BoundMethodWeakref(target=target, on_delete=on_delete)
return reference
- if callable(on_delete):
- return weakref.ref(target, on_delete)
- else:
- return weakref.ref(target)
class BoundMethodWeakref(object):
@@ -170,10 +180,12 @@ class BoundMethodWeakref(object):
'cleanup function %s: %s' % (self, function, e))
self.deletion_methods = [on_delete]
self.key = self.calculate_key(target)
- self.weak_self = weakref.ref(target.im_self, remove)
- self.weak_func = weakref.ref(target.im_func, remove)
- self.self_name = str(target.im_self)
- self.func_name = str(target.im_func.__name__)
+ im_self = get_self(target)
+ im_func = get_func(target)
+ self.weak_self = weakref.ref(im_self, remove)
+ self.weak_func = weakref.ref(im_func, remove)
+ self.self_name = str(im_self)
+ self.func_name = str(im_func.__name__)
def calculate_key(cls, target):
"""Calculate the reference key for this reference.
@@ -181,7 +193,7 @@ class BoundMethodWeakref(object):
Currently this is a two-tuple of the id()'s of the target
object and the target function respectively.
"""
- return (id(target.im_self), id(target.im_func))
+ return (id(get_self(target)), id(get_func(target)))
calculate_key = classmethod(calculate_key)
def __str__(self):
diff --git a/blinker/_utilities.py b/blinker/_utilities.py
index 4cb7cbd..9e9e037 100644
--- a/blinker/_utilities.py
+++ b/blinker/_utilities.py
@@ -133,4 +133,6 @@ def callable_reference(object, callback=None):
"""Return an annotated weak ref, supporting bound instance methods."""
if hasattr(object, 'im_self') and object.im_self is not None:
return BoundMethodWeakref(target=object, on_delete=callback)
+ elif hasattr(object, '__self__') and object.__self__ is not None:
+ return BoundMethodWeakref(target=object, on_delete=callback)
return annotatable_weakref(object, callback)
diff --git a/tests/test_saferef.py b/tests/test_saferef.py
index 3886a9e..0517fe5 100644
--- a/tests/test_saferef.py
+++ b/tests/test_saferef.py
@@ -99,9 +99,9 @@ class TestSaferef(unittest.TestCase):
sd[s] = 1
for t in self.ts:
if hasattr(t, 'x'):
- assert sd.has_key(safe_ref(t.x))
+ assert safe_ref(t.x) in sd
else:
- assert sd.has_key(safe_ref(t))
+ assert safe_ref(t) in sd
def test_Representation(self):
"""Test that the reference object's representation works
@@ -114,4 +114,3 @@ class TestSaferef(unittest.TestCase):
def _closure(self, ref):
"""Dumb utility mechanism to increment deletion counter"""
self.closure_count += 1
-