Commits

Ronald Oussoren  committed 9587084

- copy.deepcopy support is not possible after all, implementation probably needs
pickle support first.

- add custom implementations of -copyWithZone: and -mutableCopyWithZone: for
OC_PythonArray and OC_PythonDictionary (with tests)

  • Participants
  • Parent commits b1d16e1
  • Branches pyobjc-ancient

Comments (0)

Files changed (8)

File pyobjc-core/Lib/objc/_convenience.py

     ('__copy__', lambda self: self.copyWithZone_(None)),
 )
 
-NSKeyedArchiver = lookUpClass('NSKeyedArchiver')
-NSKeyedUnarchiver = lookUpClass('NSKeyedUnarchiver')
-def coder_deepcopy(self):
-    buf = NSKeyedArchiver.archivedDataWithRootObject_(self)
-    result = NSKeyedUnarchiver.unarchiveObjectWithData_(buf)
-    return result
-
-CONVENIENCE_METHODS['encodeWithCoder:'] = (
-    ('__deepcopy__', coder_deepcopy ),
-)
+# This won't work:
+#NSKeyedArchiver = lookUpClass('NSKeyedArchiver')
+#NSKeyedUnarchiver = lookUpClass('NSKeyedUnarchiver')
+#def coder_deepcopy(self, memo):
+#   buf = NSKeyedArchiver.archivedDataWithRootObject_(self)
+#   result = NSKeyedUnarchiver.unarchiveObjectWithData_(buf)
+#   return result
+#
+#CONVENIENCE_METHODS['encodeWithCoder:'] = (
+#   ('__deepcopy__', coder_deepcopy ),
+#)
 
 CLASS_METHODS['NSNull'] = (
     ('__nonzero__',  lambda self: False ),

File pyobjc-core/Lib/objc/test/test_copying.py

         self.assertEquals(o.x, 42)
         self.assertEquals(o.intVal(), 40)
 
+
+NSMutableArray = objc.lookUpClass("NSMutableArray")
+import copy
+
+class TestPyCopyObjC (objc.test.TestCase):
+    # Testcases that ensure that copy.copy works
+    # with Objective-C objects as well.
+
+    def testCopyArray(self):
+        a = NSMutableArray.arrayWithArray_(['a', 'b', 'c'])
+        self.assert_(isinstance(a, NSMutableArray))
+
+        b = copy.copy(a)
+        self.assert_(isinstance(b, NSMutableArray))
+        self.assert_(list(a) == list(b))
+
+
 if __name__ == "__main__":
     objc.test.main()

File pyobjc-core/Lib/objc/test/test_dict_proxy.py

+"""
+Minimal tests for sequence proxies
+
+NOTE: this file is very, very incomplete and just tests copying at the moment.
+"""
+import sys
+import objc.test
+from objc.test.fnd import NSDictionary, NSMutableDictionary, NSPredicate, NSObject, NSNull
+from objc.test.pythonset import OC_TestSet
+import objc
+
+OC_PythonDictionary = objc.lookUpClass("OC_PythonDictionary")
+
+
+
+
+
+class TestMutableSequence (objc.test.TestCase):
+    mapClass = dict
+
+    def testCopy(self):
+        s = self.mapClass()
+        o = OC_TestSet.set_copyWithZone_(s, None)
+        self.assertEquals(s, o)
+        self.assert_(s is not o)
+
+        s = self.mapClass({1:2, 'a':'c'})
+        o = OC_TestSet.set_copyWithZone_(s, None)
+        self.assertEquals(s, o)
+        self.assert_(s is not o)
+
+    def testProxyClass(self):
+        # Ensure that the right class is used to proxy sets
+        self.assert_(OC_TestSet.classOf_(self.mapClass()) is OC_PythonDictionary)
+
+    def testMutableCopy(self):
+
+        s = self.mapClass({1:2, 'a':'c'})
+        o = OC_TestSet.set_mutableCopyWithZone_(s, None)
+        self.assertEquals(dict(s), o)
+        self.assert_(s is not o)
+        self.assert_(isinstance(o, dict))
+
+        s = self.mapClass()
+        o = OC_TestSet.set_mutableCopyWithZone_(s, None)
+        self.assertEquals(dict(s), o)
+        self.assert_(s is not o)
+        self.assert_(isinstance(o, dict))
+
+
+
+
+if __name__ == "__main__":
+    objc.test.main()

File pyobjc-core/Lib/objc/test/test_list_proxy.py

+"""
+Minimal tests for sequence proxies
+
+NOTE: this file is very, very incomplete and just tests copying at the moment.
+"""
+import sys
+import objc.test
+from objc.test.fnd import NSArray, NSMutableArray, NSPredicate, NSObject, NSNull
+from objc.test.pythonset import OC_TestSet
+import objc
+
+OC_PythonArray = objc.lookUpClass("OC_PythonArray")
+
+class BasicSequenceTests:
+    # Tests for sets that don't try to mutate the set.
+    # Shared between tests for set() and frozenset()
+    seqClass = None
+
+    def testProxyClass(self):
+        # Ensure that the right class is used to proxy sets
+        self.assert_(OC_TestSet.classOf_(self.seqClass()) is OC_PythonArray)
+
+    def testMutableCopy(self):
+
+        s = self.seqClass(range(20))
+        o = OC_TestSet.set_mutableCopyWithZone_(s, None)
+        self.assertEquals(list(s), o)
+        self.assert_(s is not o)
+        self.assert_(isinstance(o, list))
+
+        s = self.seqClass()
+        o = OC_TestSet.set_mutableCopyWithZone_(s, None)
+        self.assertEquals(list(s), o)
+        self.assert_(s is not o)
+        self.assert_(isinstance(o, list))
+
+
+
+
+class TestImmutableSequence (objc.test.TestCase, BasicSequenceTests):
+    seqClass = tuple
+
+    def testCopy(self):
+        s = self.seqClass()
+        o = OC_TestSet.set_copyWithZone_(s, None)
+        self.assertEquals(s, o)
+
+        s = self.seqClass(range(20))
+        o = OC_TestSet.set_copyWithZone_(s, None)
+        self.assertEquals(s, o)
+
+    def testNotMutable(self):
+        # Ensure that a frozenset cannot be mutated
+        o = self.seqClass([1,2,3])
+        self.assertRaises((TypeError, AttributeError),
+                OC_TestSet.set_addObject_, o, 4)
+
+
+class TestMutableSequence (objc.test.TestCase, BasicSequenceTests):
+    seqClass = list
+
+    def testCopy(self):
+        s = self.seqClass()
+        o = OC_TestSet.set_copyWithZone_(s, None)
+        self.assertEquals(s, o)
+        self.assert_(s is not o)
+
+        s = self.seqClass(range(20))
+        o = OC_TestSet.set_copyWithZone_(s, None)
+        self.assertEquals(s, o)
+        self.assert_(s is not o)
+
+
+
+
+if __name__ == "__main__":
+    objc.test.main()

File pyobjc-core/Modules/objc/OC_PythonArray.m

 }
 
 
-#if 1
--(NSObject*)replacementObjectForArchiver:(NSArchiver*)archiver 
+-(id)copyWithZone:(NSZone*)zone
 {
-	(void)(archiver);
-	return self;
+	if (PyObjC_CopyFunc) {
+		PyObjC_BEGIN_WITH_GIL
+			PyObject* copy = PyObject_CallFunctionObjArgs(PyObjC_CopyFunc,
+					value, NULL);
+
+			if (copy == NULL) {
+				PyObjC_GIL_FORWARD_EXC();
+			} 
+
+			NSObject* result = PyObjC_PythonToId(copy);
+			Py_DECREF(copy);
+
+			if (PyErr_Occurred()) {
+				PyObjC_GIL_FORWARD_EXC();
+			}
+
+			[result retain];
+
+			PyObjC_GIL_RETURN(result);
+
+		PyObjC_END_WITH_GIL
+	} else {
+		return [super copyWithZone:zone];
+	}
 }
 
--(NSObject*)replacementObjectForKeyedArchiver:(NSKeyedArchiver*)archiver
+-(id)mutableCopyWithZone:(NSZone*)zone
 {
-	(void)(archiver);
-	return self;
+	if (PyObjC_CopyFunc) {
+		PyObjC_BEGIN_WITH_GIL
+			PyObject* copy = PySequence_List(value);
+			if (copy == NULL) {
+				PyObjC_GIL_FORWARD_EXC();
+			} 
+
+			NSObject* result = PyObjC_PythonToId(copy);
+			Py_DECREF(copy);
+
+			if (PyErr_Occurred()) {
+				PyObjC_GIL_FORWARD_EXC();
+			}
+
+			[result retain];
+			PyObjC_GIL_RETURN(result);
+
+		PyObjC_END_WITH_GIL
+	} else {
+		return [super mutableCopyWithZone:zone];
+	}
 }
-
--(NSObject*)replacementObjectForCoder:(NSKeyedArchiver*)archiver
-{
-	(void)(archiver);
-	return self;
-}
-
--(NSObject*)replacementObjectForPortCoder:(NSKeyedArchiver*)archiver
-{
-	(void)(archiver);
-	return self;
-}
-
--(Class)classForArchiver
-{
-	return [OC_PythonArray class];
-}
-
--(Class)classForKeyedArchiver
-{
-	return [OC_PythonArray class];
-}
-
--(Class)classForCoder
-{
-	return [OC_PythonArray class];
-}
-
--(Class)classForPortCoder
-{
-	return [OC_PythonArray class];
-}
-#endif
-
 @end /* implementation OC_PythonArray */

File pyobjc-core/Modules/objc/OC_PythonDictionary.m

 	}
 }
 
+-(id)copyWithZone:(NSZone*)zone
+{
+	if (PyObjC_CopyFunc) {
+		PyObjC_BEGIN_WITH_GIL
+			PyObject* copy = PyObject_CallFunctionObjArgs(PyObjC_CopyFunc,
+					value, NULL);
+			if (copy == NULL) {
+				PyObjC_GIL_FORWARD_EXC();
+			} 
 
-#if 1
+			NSObject* result = PyObjC_PythonToId(copy);
+			Py_DECREF(copy);
 
--(NSObject*)replacementObjectForArchiver:(NSArchiver*)archiver
-{
-	(void)archiver;
-	return self;
+			if (PyErr_Occurred()) {
+				PyObjC_GIL_FORWARD_EXC();
+			}
+
+			[result retain];
+
+			PyObjC_GIL_RETURN(result);
+
+		PyObjC_END_WITH_GIL
+	} else {
+		return [super copyWithZone:zone];
+	}
 }
 
--(NSObject*)replacementObjectForKeyedArchiver:(NSKeyedArchiver*)archiver
+-(id)mutableCopyWithZone:(NSZone*)zone
 {
-	(void)archiver;
-	return self;
+	if (PyObjC_CopyFunc) {
+		PyObjC_BEGIN_WITH_GIL
+			PyObject* copy = PyDict_New();
+			if (copy == NULL) {
+				PyObjC_GIL_FORWARD_EXC();
+			} 
+
+			int r = PyDict_Update(copy, value);
+			if (r == -1) {
+				PyObjC_GIL_FORWARD_EXC();
+			} 
+
+			NSObject* result = PyObjC_PythonToId(copy);
+			Py_DECREF(copy);
+
+			if (PyErr_Occurred()) {
+				PyObjC_GIL_FORWARD_EXC();
+			}
+
+			[result retain];
+
+			PyObjC_GIL_RETURN(result);
+
+		PyObjC_END_WITH_GIL
+	} else {
+		return [super mutableCopyWithZone:zone];
+	}
 }
 
 
--(Class)classForArchiver
-{
-	return [OC_PythonDictionary class];
-}
-
--(Class)classForKeyedArchiver
-{
-	return [OC_PythonDictionary class];
-}
-
--(Class)classForCoder
-{
-	return [OC_PythonDictionary class];
-}
-
--(Class)classForPortCoder
-{
-	return [OC_PythonDictionary class];
-}
-
-#endif
-
 @end  // interface OC_PythonDictionary

File pyobjc-core/Modules/objc/OC_PythonObject.m

 
 	} else {
 		PyObjC_BEGIN_WITH_GIL
-			copy = PyObject_CallFunction(PyObjC_CopyFunc, "O", pyObject);
+			copy = PyObject_CallFunctionObjArgs(PyObjC_CopyFunc, pyObject, NULL);
 			if (copy == NULL) {
 				PyObjC_GIL_FORWARD_EXC();
 			}

File pyobjc-core/NEWS.txt

 - Objective-C classes that support the ``NSCopying`` protocol can now be
   copied using ``copy.copy`` as well.
 
-- Objective-C classes that support the ``NSCoding`` protocol can now be
-  copied using ``copy.deepcopy``.
+.. 
+   it would be nice to have the following, but that's not easy to achieve::
+	- Objective-C classes that support the ``NSCoding`` protocol can now be
+	  copied using ``copy.deepcopy``.
+
+- ``OC_PythonArray`` and ``OC_PythonDictionary`` now explicitly implement
+  ``copyWithZone:`` and ``mutableCopyWithZone:``, copies will now be 
+  Python objects instead of regular ``NSDictionary`` instances.
 
 - Pure Python objects now support the ``NSCopying`` protocol.