Commits

Ronald Oussoren committed 69a2cf5

Issue #77: Passing a bound selector as a block argument failed when the block
was actually called because the trampoline that calls back to Python accidently
ignored the bound ``self`` argument.

Closes issue 77

  • Participants
  • Parent commits ad97cf6

Comments (0)

Files changed (4)

pyobjc-core/Modules/objc/libffi_support.m

         Py_DECREF(v);
     }
 
+    /* Avoid calling a PyObjCPythonSelector directory, it does
+     * additional work that we don't need.
+     */
+    if (PyObjCPythonSelector_Check(callable)) {
+        if (((PyObjCSelector*)callable)->sel_self != NULL) {
+            if (PyList_Insert(arglist, 0, ((PyObjCSelector*)callable)->sel_self) < 0) {
+                Py_DECREF(arglist);
+                goto error;
+            }
+        }
+        callable = ((PyObjCPythonSelector*)callable)->callable;
+    }
+
     v = PyList_AsTuple(arglist);
     if (v == NULL) {
         Py_DECREF(arglist);
         goto error;
     }
 
-    /* Avoid calling a PyObjCPythonSelector directory, it does
-     * additional work that we don't need.
-     */
-    if (PyObjCPythonSelector_Check(callable)) {
-        callable = ((PyObjCPythonSelector*)callable)->callable;
-    }
-
     res = PyObject_Call(callable, arglist, NULL);
     Py_DECREF(arglist);
 
         }
 
     } else if (PyObjCPythonSelector_Check(callable)) {
-        return _argcount(((PyObjCPythonSelector*)callable)->callable, haveVarArgs, haveVarKwds);
+        Py_ssize_t result = _argcount(((PyObjCPythonSelector*)callable)->callable, haveVarArgs, haveVarKwds);
+        if (((PyObjCSelector*)callable)->sel_self != NULL) {
+            result -= 1;
+        }
+        return result;
 
     } else if (PyObjCNativeSelector_Check(callable)) {
         PyObjCMethodSignature* sig = PyObjCSelector_GetMetadata(callable);
-         Py_ssize_t result = Py_SIZE(sig) - 1;
-
-         Py_DECREF(sig);
-         return result;
+        Py_ssize_t result = Py_SIZE(sig) - 1;
+
+        Py_DECREF(sig);
+        if (((PyObjCSelector*)callable)->sel_self != NULL) {
+            result -= 1;
+        }
+        return result;
 
     } else {
         PyErr_Format(PyExc_TypeError,

pyobjc-core/NEWS.txt

 Version 3.0
 -----------
 
+* Issue #77: Passing a bound selector as a block argument failed when the block
+  was actually called because the trampoline that calls back to Python accidently
+  ignored the bound ``self`` argument.
+
 * Issue #76: It is now possible to pass ``None`` to a method expecting a block
   argument, as with normal object arguments the Objective-C method receives
   a ``nil`` value.

pyobjc-core/PyObjCTest/test_blocks.py

 
         self.assertEqual(lst, [42, 43])
 
+        class Helper (object):
+            def __init__(self):
+                self.values = []
+
+            def callback(self, v):
+                self.values.append(v)
+
+        helper = Helper()
+        obj.callIntBlock_withValue_(helper.callback, 42)
+        self.assertEqual(len(helper.values), 1)
+        obj.callIntBlock_withValue_(helper.callback, 43)
+        self.assertEqual(len(helper.values), 2)
+        self.assertEqual(helper.values, [42, 43])
+
+        class Helper2 (objc.lookUpClass('NSObject')):
+            def init(self):
+                self = objc.super(Helper2, self).init()
+                if self is None:
+                    return None
+                self.values = []
+                return self
+
+            def callback_(self, v):
+                self.values.append(v)
+
+        helper = Helper2.alloc().init()
+        self.assertIsNot(helper, None)
+        self.assertEqual(len(helper.values), 0)
+        obj.callIntBlock_withValue_(helper.callback_, 42)
+        self.assertEqual(len(helper.values), 1)
+        obj.callIntBlock_withValue_(helper.callback_, 43)
+        self.assertEqual(len(helper.values), 2)
+        self.assertEqual(helper.values, [42, 43])
+
     @min_os_level('10.6')
     @onlyIf(blocksEnabled, "no blocks")
     def testBlockToObjC2(self):

pyobjc-core/PyObjCTest/test_splitsig.py

             "myMethod",
             "twoargs",
             "set_helper",
+            "callback",
 
             # dictionary methods
             "get",