Commits

Ronald Oussoren committed cf3ea08

Add more tests for the ctypes/cobject conversion in opaque-pointer types and objc.objc_object

The API should now be fully tested. This also fixes some bugs in the implementation,
and adds missing functionality to opaque pointer types.

Comments (0)

Files changed (6)

pyobjc-core/Modules/objc/objc-object.m

 
 	if (cobject != NULL && PyCapsule_CheckExact(cobject)) {
 		NSObject* p = PyCapsule_GetPointer(cobject, "objc.__object__");
+		if (PyErr_Occurred()) {
+			return NULL;
+		}
 
 		return PyObjC_IdToPython(p);
 
 	} else if (c_void_p != NULL) {
 		NSObject* p;
+		PyObject* attrval;
 
-		PyObject* attrval = PyObject_GetAttrString(c_void_p, "value");
-		if (attrval == NULL) {
-			return NULL;
+		if (PyLong_Check(c_void_p)
+#if PY_MAJOR_VERSION == 2
+				|| PyInt_Check(c_void_p)
+#endif
+			) {
+				attrval = c_void_p;
+				Py_INCREF(attrval);
+		} else {
+			attrval = PyObject_GetAttrString(c_void_p, "value");
+			if (attrval == NULL) {
+				return NULL;
+			}
 		}
 
 		if (

pyobjc-core/Modules/objc/objc-runtime-compat.h

 
 #define class_addMethodList	PyObjC_class_addMethodList
 
-/* 
- * XXX: Override protocol_getMethodDescription. This is a crude hack that's added because
- * protocol_getMethodDescription sometimes gives the wrong answer (test_protocols.py).
- * I haven't found the root cause for this yet, it may or may not be a problem with PyObjC.
- */
-extern struct objc_method_description PyObjC_protocol_getMethodDescription(Protocol *p, SEL aSel, BOOL isRequiredMethod, BOOL isInstanceMethod);
-#define protocol_getMethodDescription PyObjC_protocol_getMethodDescription
-
 #endif
 
 

pyobjc-core/Modules/objc/objc-runtime-compat.m

 #endif
 #endif
 
-#if !((MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_5) &&!defined(__OBJC2__))
-#undef protocol_getMethodDescription
-struct objc_method_description 
-PyObjC_protocol_getMethodDescription(Protocol *p, SEL aSel, BOOL isRequiredMethod, BOOL isInstanceMethod)
-{
-	struct objc_method_description  result = protocol_getMethodDescription(p, aSel, isRequiredMethod, isInstanceMethod);
-	if (result.name != NULL) {
-		return result;
-	}
-
-	{
-		/* This code should not be necessary.... 
-		 * 
-		 * for some reason the protocols created by PyObjC don't always work, without this
-		 * function test_protocol.py sometimes (but not always!) fails.
-		 */
-
-		struct objc_method_description* methods;
-		unsigned int count, i;
-		methods = protocol_copyMethodDescriptionList(p, isRequiredMethod, isInstanceMethod, &count);
-		if (methods == NULL) {
-			return result;
-		}
-
-		for (i = 0; i < count; i++) {
-			if (sel_isEqual(methods[i].name, aSel)) {
-				result = methods[i];
-				free(methods);
-				return result;
-			}
-		}
-		free(methods);
-		return result;
-
-	}
-}
-#endif
-
-
-
 void PyObjC_SetupRuntimeCompat(void)
 {
 #if (MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_5)  && !defined(__OBJC2__)

pyobjc-core/Modules/objc/opaque-pointer.m

 static PyObject* 
 opaque_new(PyTypeObject* type, PyObject* args, PyObject* kwds)
 {
-static  char* keywords[] = { "cobject", NULL };
-	PyObject* arg = NULL;
+static  char* keywords[] = { "cobject", "c_void_p", NULL };
+	PyObject* cobject = NULL;
+	PyObject* c_void_p = NULL;
 
-	if (!PyArg_ParseTupleAndKeywords(args, kwds, "|O", keywords, &arg)) {
+	if (!PyArg_ParseTupleAndKeywords(args, kwds, "|OO", keywords, &cobject, &c_void_p)) {
 		return NULL;
 	}
 
-	if (arg == NULL || !PyCapsule_CheckExact(arg)) {
-		PyErr_Format(PyExc_TypeError, "Cannot create %s objects",
-				type->tp_name);
+	if (cobject != NULL && c_void_p != NULL) {
+		PyErr_SetString(PyExc_ValueError,
+			"pass 'cobject' or 'c_void_p', not both");
 		return NULL;
-	} else {
+	}
+
+	if (cobject != NULL) {
 		OpaquePointerObject* result;
+		void* p;
+		if (!PyCapsule_CheckExact(cobject)) {
+			PyErr_SetString(PyExc_TypeError, 
+			 	"'cobject' argument is not a PyCapsule");
+			return NULL;
+		}
+		p = PyCapsule_GetPointer(cobject, "objc.__opaque__");
+		if (PyErr_Occurred()) {
+			return NULL;
+		}
+
 
 		result = PyObject_New(OpaquePointerObject, type);
 		if (result == NULL) {
 			return NULL;
 		}
-		result->pointer_value = PyCapsule_GetPointer(arg, "objc.__opaque__");
+		result->pointer_value = p;
 		return (PyObject*)result;
+
+	} else if (c_void_p != NULL) {
+		OpaquePointerObject* result;
+		void* p;
+		PyObject* attrval;
+
+		if (PyLong_Check(c_void_p)
+#if PY_MAJOR_VERSION == 2
+				|| PyInt_Check(c_void_p)
+#endif
+			) {
+				attrval = c_void_p;
+				Py_INCREF(attrval);
+		} else {
+			attrval = PyObject_GetAttrString(c_void_p, "value");
+			if (attrval == NULL) {
+				return NULL;
+			}
+		}
+
+		if (
+#if PY_MAJOR_VERSION == 2
+			PyInt_Check(attrval) ||
+			/* NOTE: PyLong_AsVoidPtr works on Int objects as well */
+#endif /* PY_MAJOR_VERSION == 2 */
+			PyLong_Check(attrval)
+		) {
+			p = PyLong_AsVoidPtr(attrval);
+			if (p == NULL && PyErr_Occurred()) {
+				Py_DECREF(attrval);
+				return NULL;
+			}
+
+		} else {
+			PyErr_SetString(PyExc_ValueError,
+				"c_void_p.value is not an integer");
+			return NULL;
+		}
+		Py_DECREF(attrval);
+		result = PyObject_New(OpaquePointerObject, type);
+		if (result == NULL) {
+			return NULL;
+		}
+		result->pointer_value = p;
+		return (PyObject*)result;
+		
+
+	} else {
+		PyErr_Format(PyExc_TypeError, "Cannot create %s objects",
+				type->tp_name);
+		return NULL;
 	}
 }
 

pyobjc-core/Modules/objc/test/pointersupport.m

+/*
+ * Helper methods opaque-pointer tests (objc.test.test_opaque)
+ */
+#include "Python.h"
+#include "pyobjc-api.h"
+#include <stdarg.h>
+
+#import <Foundation/Foundation.h>
+
+static PyObject*
+make_opaque_capsule(PyObject* mod __attribute__((__unused__)))
+{
+	return PyCapsule_New((void*)1234, "objc.__opaque__", NULL);
+}
+
+static PyObject*
+make_object_capsule(PyObject* mod __attribute__((__unused__)))
+{
+	NSObject* object = [[[NSObject alloc] init] autorelease];
+	return PyCapsule_New(object, "objc.__object__", NULL);
+}
+
+
+static PyMethodDef mod_methods[] = {
+	{
+		"opaque_capsule",
+		(PyCFunction)make_opaque_capsule,
+		METH_NOARGS,
+		0,
+	},
+	{
+		"object_capsule",
+		(PyCFunction)make_object_capsule,
+		METH_NOARGS,
+		0,
+	},
+	{ 0, 0, 0, 0 }
+};
+
+#if PY_VERSION_HEX >= 0x03000000
+
+static struct PyModuleDef mod_module = {
+	PyModuleDef_HEAD_INIT,
+	"pointersupport",
+	NULL,
+	0,
+	mod_methods,
+	NULL,
+	NULL,
+	NULL,
+	NULL
+};
+
+#define INITERROR() return NULL
+#define INITDONE() return m
+
+PyObject* PyInit_pointersupport(void);
+
+PyObject*
+PyInit_pointersupport(void)
+
+#else
+
+#define INITERROR() return
+#define INITDONE() return
+
+void initpointersupport(void);
+
+void
+initpointersupport(void)
+#endif
+{
+	PyObject* m;
+
+#if PY_VERSION_HEX >= 0x03000000
+	m = PyModule_Create(&mod_module);
+#else
+	m = Py_InitModule4("pointersupport", mod_methods,
+		NULL, NULL, PYTHON_API_VERSION);
+#endif
+	if (!m) {
+		INITERROR();
+	}
+
+	if (PyObjC_ImportAPI(m) < 0) {
+		INITERROR();
+	}
+
+	INITDONE();
+}

pyobjc-core/PyObjCTest/test_pointer_compat.py

 from PyObjCTools.TestSupport import *
+from PyObjCTest.pointersupport import opaque_capsule, object_capsule
 import objc
 import ctypes
 
+OpaqueType = objc.createOpaquePointerType("OpaqueType", b"^{OpaqueType}", None)
+
 
 class TestProxySupport (TestCase):
     def test_cobject_roundtrip(self):
         v = objc.objc_object(c_void_p=p)
         self.assertIs(v, arr)
 
-    @expectedFailure
-    def test_missing(self):
-        # Add tests simular to the ones above, but
-        # with objects created outside of PyObjC
-        self.fail()
+    def test_voidp_using_ctypes(self):
+        lib = ctypes.CDLL('/System/Library/Frameworks/CoreFoundation.framework/CoreFoundation')
+        func = lib.CFStringCreateWithCString
+        func.restype = ctypes.c_void_p
+        func.argtypes = [ctypes.c_void_p, ctypes.c_char_p, ctypes.c_int]
+
+        kCFStringEncodingISOLatin1 = 0x0201
+        ct_obj = func(None, b"hello world", kCFStringEncodingISOLatin1)
+        
+        value = objc.objc_object(c_void_p=ct_obj)
+        self.assertIsInstance(value, objc.pyobjc_unicode)
+        self.assertEqual(objc.pyobjc_id(value.nsstring()), ct_obj)
+
+    def test_opaque_capsule(self):
+        cap = opaque_capsule()
+
+        value = OpaqueType(cobject=cap)
+        self.assertIsInstance(value, OpaqueType)
+        self.assertEqual(value.__pointer__, 1234)
+
+        self.assertRaises(ValueError, OpaqueType, cobject=object_capsule())
+        self.assertRaises(TypeError, OpaqueType, cobject=42)
+    
+    def test_opaque_ctypes(self):
+        ptr = ctypes.c_void_p(0xabcd)
+
+        value = OpaqueType(c_void_p=ptr)
+        self.assertIsInstance(value, OpaqueType)
+        self.assertEqual(value.__pointer__, 0xabcd)
+
+        value = OpaqueType(c_void_p=0xdefa)
+        self.assertIsInstance(value, OpaqueType)
+        self.assertEqual(value.__pointer__, 0xdefa)
+
+
+    def test_object_capsule(self):
+        NSObject = objc.lookUpClass('NSObject')
+        cap = object_capsule()
+
+        value = NSObject(cobject=cap)
+        self.assertIsInstance(value, NSObject)
+
+        self.assertRaises(ValueError, NSObject, cobject=opaque_capsule())
+        self.assertRaises(TypeError, NSObject, cobject=42)
 
 if __name__ == "__main__":
     main()
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.