Use __self__ from bound function as allows us to provide instance as class type for class method and None for static method.
This commit is contained in:
@@ -1602,6 +1602,8 @@ static PyObject *WraptBoundFunctionWrapper_call(
|
||||
PyObject *call_args = NULL;
|
||||
PyObject *param_kwds = NULL;
|
||||
|
||||
PyObject *instance = NULL;
|
||||
|
||||
PyObject *result = NULL;
|
||||
|
||||
Py_ssize_t len = 0;
|
||||
@@ -1612,16 +1614,31 @@ static PyObject *WraptBoundFunctionWrapper_call(
|
||||
kwds = param_kwds;
|
||||
}
|
||||
|
||||
/*
|
||||
* We actually ignore the instance supplied when the function was
|
||||
* bound and use that saved against __self__ of the bound function.
|
||||
* This will be the class type for a class method and None for the
|
||||
* case of a static method.
|
||||
*/
|
||||
|
||||
instance = PyObject_GetAttrString(self->object_proxy.wrapped, "__self__");
|
||||
|
||||
if (!instance) {
|
||||
PyErr_Clear();
|
||||
Py_INCREF(Py_None);
|
||||
instance = Py_None;
|
||||
}
|
||||
|
||||
len = PySequence_Size(self->wrapper_args);
|
||||
call_args = PyTuple_New(len+4);
|
||||
|
||||
Py_INCREF(self->object_proxy.wrapped);
|
||||
Py_INCREF(self->instance);
|
||||
Py_INCREF(instance);
|
||||
Py_INCREF(args);
|
||||
Py_INCREF(kwds);
|
||||
|
||||
PyTuple_SET_ITEM(call_args, 0, self->object_proxy.wrapped);
|
||||
PyTuple_SET_ITEM(call_args, 1, self->instance);
|
||||
PyTuple_SET_ITEM(call_args, 1, instance);
|
||||
PyTuple_SET_ITEM(call_args, 2, args);
|
||||
PyTuple_SET_ITEM(call_args, 3, kwds);
|
||||
|
||||
@@ -1637,6 +1654,8 @@ static PyObject *WraptBoundFunctionWrapper_call(
|
||||
Py_DECREF(call_args);
|
||||
Py_XDECREF(param_kwds);
|
||||
|
||||
Py_DECREF(instance);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
@@ -330,7 +330,22 @@ class _BoundFunctionWrapper(ObjectProxy):
|
||||
self._self_wrapper_kwargs = kwargs
|
||||
|
||||
def __call__(self, *args, **kwargs):
|
||||
return self._self_wrapper(self._self_wrapped, self._self_instance,
|
||||
# As in this case we would be dealing with a class method or
|
||||
# static method, then _self_instance will only tell us whether
|
||||
# when calling the class or static method they did it via an
|
||||
# instance of the class it is bound to and not the case where
|
||||
# done by the class type itself. We thus ignore __self_instance
|
||||
# and use the __self__ attribute of the bound function instead.
|
||||
# For a class method, this means instance will be the class type
|
||||
# and for a static method it will be None. This is probably the
|
||||
# more useful thing we can pass through even though we loose
|
||||
# knowledge of whether they were called on the instance vs the
|
||||
# class type, as it reflects what they have available in the
|
||||
# decoratored function.
|
||||
|
||||
instance = getattr(self._self_wrapped, '__self__', None)
|
||||
|
||||
return self._self_wrapper(self._self_wrapped, instance,
|
||||
args, kwargs, *self._self_wrapper_args,
|
||||
**self._self_wrapper_kwargs)
|
||||
|
||||
|
||||
@@ -128,7 +128,7 @@ class TestCallingInnerClassMethod(unittest.TestCase):
|
||||
|
||||
@wrapt.decorator
|
||||
def _decorator(wrapped, instance, args, kwargs):
|
||||
self.assertEqual(instance, None)
|
||||
self.assertEqual(instance, Class)
|
||||
self.assertEqual(args, _args)
|
||||
self.assertEqual(kwargs, _kwargs)
|
||||
return wrapped(*args, **kwargs)
|
||||
@@ -148,16 +148,14 @@ class TestCallingInnerClassMethod(unittest.TestCase):
|
||||
self.assertEqual(result, (_args, _kwargs))
|
||||
|
||||
def test_instance_call_function(self):
|
||||
# Test calling classmethod via class instance. The instance
|
||||
# passed to the wrapper will not be None because our decorator
|
||||
# surrounds the classmethod decorator.
|
||||
# Test calling classmethod via class instance.
|
||||
|
||||
_args = (1, 2)
|
||||
_kwargs = { 'one': 1, 'two': 2 }
|
||||
|
||||
@wrapt.decorator
|
||||
def _decorator(wrapped, instance, args, kwargs):
|
||||
self.assertNotEqual(instance, None)
|
||||
self.assertEqual(instance, Class)
|
||||
self.assertEqual(args, _args)
|
||||
self.assertEqual(kwargs, _kwargs)
|
||||
return wrapped(*args, **kwargs)
|
||||
@@ -184,7 +182,7 @@ class TestCallingInnerClassMethod(unittest.TestCase):
|
||||
|
||||
@wrapt.decorator
|
||||
def _decorator(wrapped, instance, args, kwargs):
|
||||
self.assertEqual(instance, None)
|
||||
self.assertEqual(instance, Class)
|
||||
self.assertEqual(args, _args)
|
||||
self.assertEqual(kwargs, _kwargs)
|
||||
return wrapped(*args, **kwargs)
|
||||
@@ -205,16 +203,14 @@ class TestCallingInnerClassMethod(unittest.TestCase):
|
||||
self.assertEqual(result, (_args, _kwargs))
|
||||
|
||||
def test_instance_call_function_nested_decorators(self):
|
||||
# Test calling classmethod via class instance. The instance
|
||||
# passed to the wrapper will not be None because our decorator
|
||||
# surrounds the classmethod decorator.
|
||||
# Test calling classmethod via class instance.
|
||||
|
||||
_args = (1, 2)
|
||||
_kwargs = { 'one': 1, 'two': 2 }
|
||||
|
||||
@wrapt.decorator
|
||||
def _decorator(wrapped, instance, args, kwargs):
|
||||
self.assertNotEqual(instance, None)
|
||||
self.assertEqual(instance, Class)
|
||||
self.assertEqual(args, _args)
|
||||
self.assertEqual(kwargs, _kwargs)
|
||||
return wrapped(*args, **kwargs)
|
||||
|
||||
@@ -148,16 +148,14 @@ class TestCallingInnerStaticMethod(unittest.TestCase):
|
||||
self.assertEqual(result, (_args, _kwargs))
|
||||
|
||||
def test_instance_call_function(self):
|
||||
# Test calling staticmethod via class instance. The instance
|
||||
# passed to the wrapper will not be None because our decorator
|
||||
# surrounds the staticmethod decorator.
|
||||
# Test calling staticmethod via class instance.
|
||||
|
||||
_args = (1, 2)
|
||||
_kwargs = { 'one': 1, 'two': 2 }
|
||||
|
||||
@wrapt.decorator
|
||||
def _decorator(wrapped, instance, args, kwargs):
|
||||
self.assertNotEqual(instance, None)
|
||||
self.assertEqual(instance, None)
|
||||
self.assertEqual(args, _args)
|
||||
self.assertEqual(kwargs, _kwargs)
|
||||
return wrapped(*args, **kwargs)
|
||||
@@ -205,16 +203,14 @@ class TestCallingInnerStaticMethod(unittest.TestCase):
|
||||
self.assertEqual(result, (_args, _kwargs))
|
||||
|
||||
def test_instance_call_function_nested_decorator(self):
|
||||
# Test calling staticmethod via class instance. The instance
|
||||
# passed to the wrapper will not be None because our decorator
|
||||
# surrounds the staticmethod decorator.
|
||||
# Test calling staticmethod via class instance.
|
||||
|
||||
_args = (1, 2)
|
||||
_kwargs = { 'one': 1, 'two': 2 }
|
||||
|
||||
@wrapt.decorator
|
||||
def _decorator(wrapped, instance, args, kwargs):
|
||||
self.assertNotEqual(instance, None)
|
||||
self.assertEqual(instance, None)
|
||||
self.assertEqual(args, _args)
|
||||
self.assertEqual(kwargs, _kwargs)
|
||||
return wrapped(*args, **kwargs)
|
||||
|
||||
Reference in New Issue
Block a user