diff --git a/docs/changes.rst b/docs/changes.rst index e6bf0d2..3273d21 100644 --- a/docs/changes.rst +++ b/docs/changes.rst @@ -10,6 +10,10 @@ Version 1.10.5 target modules were registered in the same entry point list. Only the callback for the last would be called regardless of the target module. +* If a ``WeakFunctionProxy`` wrapper was used around a method of a class + which was decorated using a wrapt decorator, the decorator wasn't being + invoked when the method was called via the weakref proxy. + **Features Changed** * The ``register_post_import_hook()`` function, modelled after the diff --git a/src/_wrappers.c b/src/_wrappers.c index e28e1bf..9194f54 100644 --- a/src/_wrappers.c +++ b/src/_wrappers.c @@ -15,6 +15,7 @@ typedef struct { PyObject *dict; PyObject *wrapped; + PyObject *weakreflist; } WraptObjectProxyObject; PyTypeObject WraptObjectProxy_Type; @@ -48,6 +49,7 @@ static PyObject *WraptObjectProxy_new(PyTypeObject *type, self->dict = PyDict_New(); self->wrapped = NULL; + self->weakreflist = NULL; return (PyObject *)self; } @@ -153,6 +155,9 @@ static void WraptObjectProxy_dealloc(WraptObjectProxyObject *self) { PyObject_GC_UnTrack(self); + if (self->weakreflist != NULL) + PyObject_ClearWeakRefs((PyObject *)self); + WraptObjectProxy_clear(self); Py_TYPE(self)->tp_free(self); @@ -1683,7 +1688,7 @@ PyTypeObject WraptObjectProxy_Type = { (traverseproc)WraptObjectProxy_traverse, /*tp_traverse*/ (inquiry)WraptObjectProxy_clear, /*tp_clear*/ (richcmpfunc)WraptObjectProxy_richcompare, /*tp_richcompare*/ - 0, /*tp_weaklistoffset*/ + offsetof(WraptObjectProxyObject, weakreflist), /*tp_weaklistoffset*/ (getiterfunc)WraptObjectProxy_iter, /*tp_iter*/ 0, /*tp_iternext*/ WraptObjectProxy_methods, /*tp_methods*/ @@ -1754,7 +1759,7 @@ PyTypeObject WraptCallableObjectProxy_Type = { 0, /*tp_traverse*/ 0, /*tp_clear*/ 0, /*tp_richcompare*/ - 0, /*tp_weaklistoffset*/ + offsetof(WraptObjectProxyObject, weakreflist), /*tp_weaklistoffset*/ 0, /*tp_iter*/ 0, /*tp_iternext*/ 0, /*tp_methods*/ @@ -2249,7 +2254,7 @@ PyTypeObject WraptFunctionWrapperBase_Type = { (traverseproc)WraptFunctionWrapperBase_traverse, /*tp_traverse*/ (inquiry)WraptFunctionWrapperBase_clear, /*tp_clear*/ 0, /*tp_richcompare*/ - 0, /*tp_weaklistoffset*/ + offsetof(WraptObjectProxyObject, weakreflist), /*tp_weaklistoffset*/ 0, /*tp_iter*/ 0, /*tp_iternext*/ 0, /*tp_methods*/ @@ -2482,7 +2487,7 @@ PyTypeObject WraptBoundFunctionWrapper_Type = { 0, /*tp_traverse*/ 0, /*tp_clear*/ 0, /*tp_richcompare*/ - 0, /*tp_weaklistoffset*/ + offsetof(WraptObjectProxyObject, weakreflist), /*tp_weaklistoffset*/ 0, /*tp_iter*/ 0, /*tp_iternext*/ 0, /*tp_methods*/ @@ -2622,7 +2627,7 @@ PyTypeObject WraptFunctionWrapper_Type = { 0, /*tp_traverse*/ 0, /*tp_clear*/ 0, /*tp_richcompare*/ - 0, /*tp_weaklistoffset*/ + offsetof(WraptObjectProxyObject, weakreflist), /*tp_weaklistoffset*/ 0, /*tp_iter*/ 0, /*tp_iternext*/ 0, /*tp_methods*/ diff --git a/src/wrappers.py b/src/wrappers.py index dbc8a0f..945caaf 100644 --- a/src/wrappers.py +++ b/src/wrappers.py @@ -855,6 +855,20 @@ class WeakFunctionProxy(ObjectProxy): self._self_expired = False + if isinstance(wrapped, _FunctionWrapperBase): + self._self_instance = weakref.ref(wrapped._self_instance, + _callback) + + if wrapped._self_parent is not None: + super(WeakFunctionProxy, self).__init__( + weakref.proxy(wrapped._self_parent, _callback)) + + else: + super(WeakFunctionProxy, self).__init__( + weakref.proxy(wrapped, _callback)) + + return + try: self._self_instance = weakref.ref(wrapped.__self__, _callback) diff --git a/tests/test_weak_function_proxy.py b/tests/test_weak_function_proxy.py index 0f5bd09..d34da0e 100644 --- a/tests/test_weak_function_proxy.py +++ b/tests/test_weak_function_proxy.py @@ -172,5 +172,23 @@ class TestWeakFunctionProxy(unittest.TestCase): self.assertEqual(len(result), 1) self.assertEqual(id(proxy), result[0]) + def test_decorator_method(self): + @wrapt.decorator + def bark(wrapped, instance, args, kwargs): + return 'bark' + + class Animal(object): + @bark + def squeal(self): + return 'squeal' + + animal = Animal() + + self.assertEqual(animal.squeal(), 'bark') + + method = wrapt.WeakFunctionProxy(animal.squeal) + + self.assertEqual(method(), 'bark') + if __name__ == '__main__': unittest.main()