Anonymous avatar Anonymous committed 70bbf61

Added tests for key/value coding compliance that is in line with the Objective-C implementation. Added mix-in class to KeyValueCoding.py that can be added to any Python class to make it compatible with key/value coding when passed across the bridge into Python-- for performance reasons, it might make more sense to implement said code in the bridge itself.

Comments (0)

Files changed (4)

pyobjc/Lib/PyObjCTools/KeyValueCoding.py

         cur = getKey(cur, e)
 
     return setKey(cur, elements[-1], value)
+
+class KeyValueCodingMixIn:
+    def valueForKey_(self, aKey):
+        return getKey(self, aKey)
+
+    def takeValue_forKey_(self, aValue, aKey):
+        return setKey(self, aKey, aValue)
+
+    def valueForKeyPath_(self, aKey):
+        return getKeyPath(self, aKey)
+                    
+    def takeValue_forKeyPath_(self, aValue, aKey):
+        return setKeyPath(self, aKey, aValue)
+

pyobjc/Lib/PyObjCTools/test/test_keyvalue2.py

+"""
+Tests for PyObjCTools.KeyValueCoding
+
+TODO: 
+    - Accessing properties in superclass of ObjC hybrids (see also Foundation.test.test_keyvalue)
+
+NOTE: Testcases here should be synchronized with the Key-Value Coding tests
+in objc.test.test_keyvalue and Foundation.test.test_keyvalue.
+"""
+
+from PyObjCTools import KeyValueCoding
+import objc
+import unittest
+
+from PyObjCTools.test.testkvcodingbndl import *
+
+DirectString = u'Direct String'
+IndirectString = u'Indirect String'
+DirectNumber = 42
+IndirectNumber = 84
+
+class KVPyBase(KeyValueCoding.KeyValueCodingMixIn):
+    def __init__(self):
+        self.directString = DirectString
+        self._indirectString = IndirectNumber
+        self.directNumber = DirectNumber
+        self._indirectNumber = IndirectNumber
+
+    def indirectString(self):
+        return self._indirectString
+    def setIndirectString_(self, aString):
+        self._indirectString = aString
+
+    def indirectNumber(self):
+        return self._indirectNumber
+    def setIndirectNumber_(self, aNumber):
+        self._indirectNumber = aNumber
+
+class KVPyPath(KeyValueCoding.KeyValueCodingMixIn):
+    def __init__(self):
+        self.directHead = KVPyBase()
+        self._indirectHead = KVPyBase()
+
+    def indirectHead(self):
+        return self._indirectHead
+    def setIndirectHead_(self, aHead):
+        self._indirectHead = aHead
+
+class KVPySubObjCBase(KVBaseClass):
+    pass
+
+class KVPySubObjCPath(KVPathClass):
+    pass
+
+class KVPySubOverObjCBase(KVBaseClass):
+    def init(self):
+        super(KVPySubOverObjCBase, self).init()
+        self.overDirect = DirectString
+        self._overIndirect = IndirectString
+
+    def overIndirectString(self):
+        return self._overIndirect
+    def setOverIndirectString_(self, aString):
+        self._overIndirect = aString
+
+class KVPySubOverObjCPath(KVPathClass):
+    def init(self):
+        super(KVPySubOverObjCPath, self).init()
+        self.overDirectHead = KVPySubOverObjCBase.new()
+        self._overIndirectHead = KVPySubOverObjCBase.new()
+
+    def overIndirectHead(self):
+        return self._overIndirectHead
+    def setOverIndirectHead_(self, aHead):
+        self._overIndirectHead = aHead
+
+class AbstractKVCodingTest:
+    def testBaseCValueForKey(self):
+        self.assertEquals(DirectString, self.base.valueForKey_("directString"))
+        self.assertEquals(IndirectString, self.base.valueForKey_("indirectString"))
+        self.assertEquals(DirectNumber, self.base.valueForKey_("directNumber"))
+        self.assertEquals(IndirectNumber, self.base.valueForKey_("indirectNumber"))
+
+    def testPathValueForKey(self):
+        self.assertEquals(DirectString, self.path.valueForKeyPath_("directHead.directString"))
+        self.assertEquals(IndirectString, self.path.valueForKeyPath_("indirectHead.indirectString"))
+        self.assertEquals(DirectNumber, self.path.valueForKeyPath_("directHead.directNumber"))
+        self.assertEquals(IndirectNumber, self.path.valueForKeyPath_("indirectHead.indirectNumber"))
+        
+class TestObjCKVCoding(AbstractKVCodingTest, unittest.TestCase):
+    def setUp(self):
+        self.base = KVBaseClass.new()
+        self.path = KVPathClass.new()
+
+class TestPythonKVCoding(AbstractKVCodingTest, unittest.TestCase):
+    def setUp(self):
+        self.base = KVPyBase()
+        self.path = KVPyPath()
+
+class TestPythonSubObjCContainerCoding(AbstractKVCodingTest, unittest.TestCase):
+    def setUp(self):
+        self.base = KVPySubObjCBase.new()
+        self.path = KVPySubObjCPath.new()
+
+class TestPythonSubOverObjC(AbstractKVCodingTest, unittest.TestCase):
+    def setUp(self):
+        self.base = KVPySubOverObjCBase.new()
+        self.path = KVPySubOverObjCPath.new()
+
+    def testOverValueForKey(self):
+        self.assertEquals(DirectString, self.base.valueForKey_("overDirectString"))
+        self.assertEquals(IndirectString, self.base.valueForKey_("overIndirectString"))
+
+    def testOverValueForKeyPath(self):
+        self.assertEquals(DirectString, self.path.valueForKeyPath_("overDirectHead.directString"))
+        self.assertEquals(IndirectString, self.path.valueForKeyPath_("overIndirectHead.indirectString"))
+
+if __name__ == "__main__":
+    unittest.main()

pyobjc/Lib/PyObjCTools/test/testkvcodingbndl.m

+/*
+ * This file implements a (number of) class(es) that are used to test
+ * the Key/Value coding support within PyObjC
+ *
+ * NOTES
+ * - The implementation must be synchronized with test_keyvalue2.py, see that
+ *   file for more details.
+ */
+#import <Foundation/Foundation.h>
+
+#include <Python.h>
+#include <pyobjc-api.h>
+#include <objc/objc-runtime.h>
+
+
+@interface KVBaseClass : NSObject
+{
+    NSString *directString;
+    NSNumber *directNumber;
+    NSString *indirectString;
+    NSNumber *indirectNumber;
+}
+@end
+
+@implementation KVBaseClass
+- init
+{
+    self = [super init];
+    if (!self) return nil;
+
+    directString = [@"Direct String" retain];
+    directNumber = [[NSNumber numberWithInt: 42] retain];
+    indirectString = [@"Indirect String" retain];
+    indirectNumber = [[NSNumber numberWithInt: 84] retain];
+
+    return self;
+}
+
+- (NSString *) indirectString; { return indirectString; }
+- (void) setIndirectString: (NSString *) aString;
+{
+    [aString retain];
+    [indirectString release];
+    indirectString = aString;
+}
+
+- (NSNumber *) indirectNumber; { return indirectNumber; }
+- (void) setIndirectNumber: (NSNumber *) aNumber;
+{
+    [aNumber retain];
+    [indirectNumber release];
+    indirectNumber = aNumber;
+}
+@end
+
+@interface KVPathClass : NSObject
+{
+    KVBaseClass *directHead;
+    KVBaseClass *indirectHead;
+}
+@end
+
+@implementation KVPathClass
+- init
+{
+    self = [super init];
+    if (!self) return nil;
+
+    directHead = [[KVBaseClass alloc] init];
+    indirectHead = [[KVBaseClass alloc] init];
+
+    return self;
+}
+
+- (KVBaseClass *) indirectHead { return indirectHead; } 
+- (void) setInidrectHead: (KVBaseClass *) aHead;
+{
+    [aHead retain];
+    [indirectHead release];
+    indirectHead = aHead;
+}
+@end
+
+/* Python glue */
+
+static PyMethodDef no_methods[] = {
+	{ 0, 0, 0, 0 }
+};
+
+void inittestkvcodingbndl(void);
+void inittestkvcodingbndl(void)
+{
+    PyObject* m;
+
+    m = Py_InitModule4("testkvcodingbndl", no_methods, 
+		       NULL, NULL, PYTHON_API_VERSION);
+    if (!m) return;
+
+    if (PyObjC_ImportAPI(m) < 0) return;
+
+    PyModule_AddObject(m, "KVBaseClass", 
+		       PyObjCClass_New([KVBaseClass class]));
+    PyModule_AddObject(m, "KVPathClass", 
+		       PyObjCClass_New([KVPathClass class]));
+}
               ["Lib/objc/test/testbndl2.m"],
               extra_compile_args=["-IModules/objc" ] + CFLAGS,
               extra_link_args=OBJC_LDFLAGS),
+    Extension("PyObjCTools.test.testkvcodingbndl",
+              ["Lib/PyObjCTools/test/testkvcodingbndl.m"],
+              extra_compile_args=["-IModules/objc" ] + CFLAGS,
+              extra_link_args=OBJC_LDFLAGS),
     Extension("autoGIL", 
               ["Modules/autoGIL.c"],
               extra_compile_args = CFLAGS,
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.