Commits

Ronald Oussoren committed c8c751a

- Rework the code that ensures that proxy objects are unique. Object identity
is now preserved in both directions (see NEWS.txt for the limitations)
- Some more hackery on the number proxies:
* Use __slots__ where possible to avoid adding a __dict__ to
the proxies for NSNumbers
* Implement __class__ for those proxies as well, this way the proxies
seem to fit at the right place into the class hiearchy.
* More unittests
- NSDecimalNumber is no longer proxied to NSDecimal, because the latter
is mutable.

Comments (0)

Files changed (25)

 
 Only implement this when it is possible to convert without loss of information.
 
+**Ronald, 20050130**: *converting* is not an option: PyObjC takes care to
+preserve the identity of ObjC objects when passed through python. This is 
+necessary for at least some APIs.   Furthermore, both ``NSSet`` and 
+``__builtin__.set`` are mutable!
+
+
 Python 2.4
 ..........
 

Lib/AppKit/test/test_nsworkspace.py

 
         # A method with 2 output parameters, this means the result
         # is a tuple with 3 elements (return value, param1, param2)
-        res = ws.getInfoForFile_application_type_(
-            os.getcwd().decode('utf8'))
+        res = ws.getInfoForFile_application_type_(u'/')
         self.assert_(isinstance(res, tuple))
         self.assert_(len(res) == 3)
         self.assert_(res[0] == 1)

Lib/Foundation/test/test_nsdecimal.py

 from Foundation import *
 import operator
 import objc
+import sys
 
 class TestNSDecimal (unittest.TestCase):
     def testCreation(self):
         self.assertEquals(o.description(), u"1.1234")
 
 class NSDecimalOperators (unittest.TestCase):
+    def testCoerce(self):
+        r = NSDecimal(1)
+
+        v = coerce(r, r)
+        self.assertEquals(v, (r, r))
+
+        v = coerce(r, 2)
+        self.assertEquals(v, (r, NSDecimal(2)))
+
+        v = coerce(2, r)
+        self.assertEquals(v, (NSDecimal(2), r))
+
+        v = coerce(r, sys.maxint+2)
+        self.assertEquals(v, (r, NSDecimal(sys.maxint+2)))
+
+        v = coerce(sys.maxint+2, r)
+        self.assertEquals(v, (NSDecimal(sys.maxint+2), r))
+
+        t = NSDecimal(4).__pyobjc_object__
+        self.assert_(isinstance(t, NSObject))
+        v = coerce(t, r)
+        self.assertEquals(v, (NSDecimal(4), r))
+
+        v = coerce(r, t)
+        self.assertEquals(v, (r, NSDecimal(4)))
+
+        self.assertRaises(TypeError, coerce, 1.0, r)
+        self.assertRaises(TypeError, coerce, r, 1.0)
+        self.assertRaises(TypeError, coerce, "1.0", r)
+        self.assertRaises(TypeError, coerce, r, "1.0")
+        self.assertRaises(TypeError, coerce, u"1.0", r)
+        self.assertRaises(TypeError, coerce, r, u"1.0")
+        self.assertRaises(TypeError, coerce, (), r)
+        self.assertRaises(TypeError, coerce, r, ())
+
+
     def testAddition(self):
         r = NSDecimal()
         o = NSDecimal(1)
         p = NSDecimal(2)
 
+        O = o.__pyobjc_object__
+        P = p.__pyobjc_object__
+        self.assert_(isinstance(P, NSObject))
+        self.assert_(isinstance(O, NSObject))
+
         NSDecimalAdd(r, o, p, NSRoundPlain)
         self.assertEquals(o+p, r)
+        self.assertEquals(o+P, r)
+        self.assertEquals(O+p, r)
         self.assertEquals(o+2, r)
         self.assertEquals(o+2L, r)
-        self.assertEquals(1+p, r)
+        self.assertEquals(p+1, r)
         self.assertEquals(1+p, r)
 
         self.assertRaises(TypeError, operator.add, o, 1.2)
         self.assertRaises(TypeError, operator.add, 1.2, o)
+        self.assertRaises(TypeError, operator.add, o, "1.2")
+        self.assertRaises(TypeError, operator.add, "1.2", o)
+        self.assertRaises(TypeError, operator.add, o, u"1.2")
+        self.assertRaises(TypeError, operator.add, u"1.2", o)
+        self.assertRaises(TypeError, operator.add, o, [])
+        self.assertRaises(TypeError, operator.add, [], o)
 
     def testSubtraction(self):
         r = NSDecimal()
         o = NSDecimal(1)
         p = NSDecimal(2)
 
+        P = p.__pyobjc_object__
+        O = o.__pyobjc_object__
+        self.assert_(isinstance(P, NSObject))
+        self.assert_(isinstance(O, NSObject))
+
+
         NSDecimalSubtract(r, o, p, NSRoundPlain)
         self.assertEquals(o-p, r)
+        self.assertEquals(O-p, r)
+        self.assertEquals(o-P, r)
         self.assertEquals(o-2, r)
         self.assertEquals(o-2L, r)
         self.assertEquals(1-p, r)
 
         self.assertRaises(TypeError, operator.sub, o, 1.2)
         self.assertRaises(TypeError, operator.sub, 1.2, o)
+        self.assertRaises(TypeError, operator.sub, o, "1.2")
+        self.assertRaises(TypeError, operator.sub, "1.2", o)
+        self.assertRaises(TypeError, operator.sub, o, u"1.2")
+        self.assertRaises(TypeError, operator.sub, u"1.2", o)
+        self.assertRaises(TypeError, operator.sub, o, ())
+        self.assertRaises(TypeError, operator.sub, (), o)
 
     def testMultiplication(self):
         r = NSDecimal()
         o = NSDecimal(2)
         p = NSDecimal(3)
 
+        P = p.__pyobjc_object__
+        O = o.__pyobjc_object__
+        self.assert_(isinstance(P, NSObject))
+        self.assert_(isinstance(O, NSObject))
+
         NSDecimalMultiply(r, o, p, NSRoundPlain)
         self.assertEquals(o*p, r)
+        self.assertEquals(O*p, r)
+        self.assertEquals(o*P, r)
         self.assertEquals(o*3, r)
         self.assertEquals(o*3L, r)
         self.assertEquals(2*p, r)
         o = NSDecimal(2)
         p = NSDecimal(3)
 
+        P = p.__pyobjc_object__
+        O = o.__pyobjc_object__
+        self.assert_(isinstance(P, NSObject))
+        self.assert_(isinstance(O, NSObject))
+
         NSDecimalDivide(r, o, p, NSRoundPlain)
         self.assertEquals(o/p, r)
+        self.assertEquals(O/p, r)
+        self.assertEquals(o/P, r)
         self.assertEquals(o/3, r)
         self.assertEquals(o/3L, r)
         self.assertEquals(2/p, r)
         o = NSDecimal(1)
         p = NSDecimal(2)
 
+        P = p.__pyobjc_object__
+        self.assert_(isinstance(P, NSObject))
+
         NSDecimalAdd(r, o, p, NSRoundPlain)
 
         o = NSDecimal(1)
         self.assertEquals(o, r)
 
         o = NSDecimal(1)
+        o += P
+        self.assertEquals(o, r)
+
+        o = NSDecimal(1)
         o += 2
         self.assertEquals(o, r)
 
         o = NSDecimal(1)
         p = NSDecimal(2)
 
+        P = p.__pyobjc_object__
+        self.assert_(isinstance(P, NSObject))
+
         NSDecimalSubtract(r, o, p, NSRoundPlain)
 
         o = NSDecimal(1)
         self.assertEquals(o, r)
 
         o = NSDecimal(1)
+        o -= P
+        self.assertEquals(o, r)
+
+
+        o = NSDecimal(1)
         o -= 2
         self.assertEquals(o, r)
 
         o = NSDecimal(2)
         p = NSDecimal(3)
 
+        P = p.__pyobjc_object__
+        self.assert_(isinstance(P, NSObject))
+
         NSDecimalMultiply(r, o, p, NSRoundPlain)
 
         o = NSDecimal(2)
         self.assertEquals(o, r)
 
         o = NSDecimal(2)
+        o *= P
+        self.assertEquals(o, r)
+
+        o = NSDecimal(2)
         o *= 3
         self.assertEquals(o, r)
 
         o = NSDecimal(2)
         p = NSDecimal(3)
 
+        P = p.__pyobjc_object__
+        self.assert_(isinstance(P, NSObject))
+
         NSDecimalDivide(r, o, p, NSRoundPlain)
 
         o = NSDecimal(2)
         self.assertEquals(o, r)
 
         o = NSDecimal(2)
+        o /= P
+        self.assertEquals(o, r)
+
+        o = NSDecimal(2)
         o /= 3
         self.assertEquals(o, r)
 

Lib/Foundation/test/test_nsnumber.py

 import objc
 import re
 import sys
+import operator
 
 from Foundation import *
 
     return r.replace(u'version="0.9"', u'version="1.0"')
 
 
-# NSNumber instances are converted to Python numbers
-
 class TestNSNumber( unittest.TestCase ):
     def testSimple(self):
         self.assertEquals(NSNumber.numberWithFloat_(1.0), 1,0)
         self.assertEquals(NSNumber.numberWithFloat_(-0.5), -0.5)
         self.assertEquals(NSNumber.numberWithInt_(-4), -4)
         self.assertEquals(NSNumber.numberWithInt_(0), 0)
-        self.assertEquals(NSNumber.numberWithFloat_(0.0), 0,0)
+        self.assertEquals(NSNumber.numberWithFloat_(0.0), 0.0)
+
+    def testReadOnly(self):
+        n = NSNumber.numberWithFloat_(1.2)
+        self.assertRaises(AttributeError, setattr, n, 'foo', 2)
+
+        n = NSNumber.numberWithInt_(1)
+        self.assertRaises(AttributeError, setattr, n, 'foo', 2)
+
+        n = NSNumber.numberWithLongLong_(sys.maxint + 2)
+        self.assertRaises(AttributeError, setattr, n, 'foo', 2)
 
     def testUseAsBasicType(self):
         lstValue = list(range(0, 20, 2))
         for idx, v in enumerate(lstValue):
             self.assertEquals(v, lstValue[NSNumber.numberWithInt_(idx)])
+            self.assertEquals(v, lstValue[NSNumber.numberWithLong_(idx)])
+            self.assertEquals(v, lstValue[NSNumber.numberWithLongLong_(idx)])
+
+        self.assertRaises(TypeError, operator.getitem, lstValue,
+                NSNumber.numberWithFloat_(2.0))
 
     def testUnsignedIssues(self):
         # NSNumber stores unsigned numbers as signed numbers
-        # This is a bug in Cocoa...
+        # This is a bug in Cocoa... (RADAR #4007594)
 
         self.assertEquals(NSNumber.numberWithUnsignedInt_(sys.maxint+1),
                     -sys.maxint-1)
         v = NSNumber.numberWithInt_(10)
         self.assertEquals(v.doubleValue(), float(10))
 
+    def testMath(self):
+        Xs = list(range(10, 40, 3))
+        Ys = list(range(-12, 44, 5))
+
+        self.assert_(0 not in Ys)
+        self.assert_(0 not in Xs)
+
+        for x in Xs:
+            for y in Ys:
+                Nx = NSNumber.numberWithInt_(x)
+                Ny = NSNumber.numberWithInt_(y)
+
+                self.assertEquals(x + y, Nx + Ny)
+                self.assertEquals(x - y, Nx - Ny)
+                self.assertEquals(x * y, Nx * Ny)
+                self.assertEquals(x / y, Nx / Ny)
+                self.assertEquals(x % y, Nx % Ny)
+                self.assertEquals(x ** y, Nx ** Ny)
+
+                Nx = NSNumber.numberWithFloat_(x+0.5)
+                Ny = NSNumber.numberWithFloat_(y+0.5)
+
+                self.assertEquals((x+0.5) + (y+0.5), Nx + Ny)
+                self.assertEquals((x+0.5) - (y+0.5), Nx - Ny)
+                self.assertEquals((x+0.5) * (y+0.5), Nx * Ny)
+                self.assertEquals((x+0.5) / (y+0.5), Nx / Ny)
+                self.assertEquals((x+0.5) % (y+0.5), Nx % Ny)
+                self.assertEquals((x+0.5) ** (y+0.5), Nx ** Ny)
+
+                Nx = NSNumber.numberWithLongLong_(x)
+                Ny = NSNumber.numberWithLongLong_(y)
+
+                self.assertEquals(long(x) + long(y), Nx + Ny)
+                self.assertEquals(long(x) - long(y), Nx - Ny)
+                self.assertEquals(long(x) * long(y), Nx * Ny)
+                self.assertEquals(long(x) / long(y), Nx / Ny)
+                self.assertEquals(long(x) % long(y), Nx % Ny)
+                self.assertEquals(long(x) ** long(y), Nx ** Ny)
+
+
+    def testTyping(self):
+        # Thanks to some tricks and a cooperating Python runtime,
+        # NSNumber "instances" seem to be subclasses of both NSNumber and
+        # the corresponding Python number type.
+        #
+
+        n = NSNumber.numberWithInt_(10)
+        self.assert_(isinstance(n, int))
+        self.assert_(isinstance(n, NSNumber))
+
+        n = NSNumber.numberWithUnsignedInt_(10)
+        self.assert_(isinstance(n, int))
+        self.assert_(isinstance(n, NSNumber))
+
+        n = NSNumber.numberWithLong_(10)
+        self.assert_(isinstance(n, int))
+        self.assert_(isinstance(n, NSNumber))
+
+        n = NSNumber.numberWithUnsignedLong_(10)
+        self.assert_(isinstance(n, int))
+        self.assert_(isinstance(n, NSNumber))
+
+        n = NSNumber.numberWithLongLong_(10)
+        self.assert_(isinstance(n, long))
+        self.assert_(isinstance(n, NSNumber))
+
+        n = NSNumber.numberWithUnsignedLongLong_(10)
+        self.assert_(isinstance(n, long))
+        self.assert_(isinstance(n, NSNumber))
+
+        n = NSNumber.numberWithFloat_(10)
+        self.assert_(isinstance(n, float))
+        self.assert_(isinstance(n, NSNumber))
+
+        n = NSNumber.numberWithDouble_(10)
+        self.assert_(isinstance(n, float))
+        self.assert_(isinstance(n, NSNumber))
+
+
 if objc.platform == 'MACOSX':
     class TestPropList (unittest.TestCase):
         #Test if numbers are stored properly in property-list. The most

Lib/Foundation/test/test_nsstring.py

         curEnabledFlag = objc.getStrBridgeEnabled()
         objc.setStrBridgeEnabled(True)
         try:
-            v = NSString.stringWithString_("hello")
+            v = NSString.stringWithString_("hello2")
             self.assert_(isinstance(v, objc.pyobjc_unicode))
-            self.assertEquals(v, u"hello")
+            self.assertEquals(v, u"hello2")
 
 
             self.assertRaises(UnicodeError, unicode, "\xff")
             try:
                 #v = NSString.stringWithString_("hello")
                 self.assertRaises(objc.PyObjCStrBridgeWarning,
-                        NSString.stringWithString_, "hello")
+                        NSString.stringWithString_, "hello2")
 
             finally:
                 del warnings.filters[0]

Lib/objc/__init__.py

 _update()
 del _update
 
+from _convenience import *
 import _FoundationSignatures
 
 # Add useful utility functions below
 
 from _protocols import *
 from _descriptors import *
-from _convenience import *
 from _category import *
 from _bridges import *
 from _compat import *

Lib/objc/_bridges.py

         return (tuple(array('B').fromstring(fsRef.data)),)
     BRIDGED_TYPES.append((FSRef, FSRef_to_struct))
 
-#try:
-#    import decimal
-#except ImportError:
-#    pass
-#else:
-#    NSDecimalNumber = lookUpClass('NSDecimalNumber')
-#    def Decimal_to_NSDecimalNumber(d):
-#        # XXX - this doesn't take into account the locale setting
-#        #       however I don't know what the heck NSDecimalNumber
-#        #       expects..
-#        return NSDecimalNumber.decimalNumberWithString_(unicode(d))
-#    BRIDGED_TYPES.append((decimal.Decimal, Decimal_to_NSDecimalNumber))
-
 def _bridgePythonTypes():
     # python TO Obj-C
     OC_PythonObject = lookUpClass('OC_PythonObject')

Lib/objc/_convenience.py

 NSObject = lookUpClass('NSObject')
 
 def add_convenience_methods(super_class, name, type_dict):
+    try:
+        return _add_convenience_methods(super_class, name, type_dict)
+    except:
+        import traceback
+        traceback.print_exc()
+        raise
+
+
+def _add_convenience_methods(super_class, name, type_dict):
     """
     Add additional methods to the type-dict of subclass 'name' of
     'super_class'.
 
         if sel in CONVENIENCE_METHODS:
             v = CONVENIENCE_METHODS[sel]
-            for name, value in v:
-                if name in type_dict and isinstance(type_dict[name], selector):
+            for nm, value in v:
+                if nm in type_dict and isinstance(type_dict[nm], selector):
 
                     # Clone attributes of already existing version
 
-                    t = type_dict[name]
+                    t = type_dict[nm]
                     v = selector(value, selector=t.selector,
                         signature=t.signature, isClassMethod=t.isClassMethod)
                     v.isAlloc = t.isAlloc
 
-                    type_dict[name] = v
+                    type_dict[nm] = v
                 else:
-                    type_dict[name] = value
+                    type_dict[nm] = value
 
     if name in CLASS_METHODS:
-        for name, value in CLASS_METHODS[name]:
-            type_dict[name] = value
+        for nm, value in CLASS_METHODS[name]:
+            type_dict[nm] = value
 
 setClassExtender(add_convenience_methods)
 
 CLASS_METHODS['NSNull'] = (
         (   '__nonzero__',  lambda self: False ),
 )
+
+NSDecimalNumber = lookUpClass('NSDecimalNumber')
+def _makeD(v): 
+    if isinstance(v, NSDecimalNumber):
+        return v
+    return NSDecimalNumber.decimalNumberWithDecimal_(v)
+
+CLASS_METHODS['NSDecimalNumber'] = (
+        ( '__add__',  lambda self, other: _makeD(self.decimalValue()+other) ),
+        ( '__radd__', lambda self, other: _makeD(other+self.decimalValue()) ),
+        ( '__sub__',  lambda self, other: _makeD(self.decimalValue()-other) ),
+        ( '__rsub__', lambda self, other: _makeD(other-self.decimalValue()) ),
+        ( '__mul__',  lambda self, other: _makeD(self.decimalValue()*other) ),
+        ( '__rmul__', lambda self, other: _makeD(other*self.decimalValue()) ),
+        ( '__div__',  lambda self, other: _makeD(self.decimalValue()/other) ),
+        ( '__rdiv__', lambda self, other: _makeD(other/self.decimalValue()) ),
+        ( '__mod__',  lambda self, other: _makeD(self.decimalValue()%other) ),
+        ( '__rmod__', lambda self, other: _makeD(other%self.decimalValue()) ),
+        ( '__neg__',  lambda self: _makeD(-(self.decimalValue())) ),
+        ( '__pos__',  lambda self: _makeD(+(self.decimalValue())) ),
+        ( '__abs__',  lambda self: _makeD(abs(self.decimalValue())) ),
+        
+)

Lib/objc/_pythonify.py

 
 __all__ = []
 
+
 class OC_PythonFloat(float):
+    __slots__=('__pyobjc_object__',)
+
+
     def __new__(cls, obj, value):
         self = float.__new__(cls, value)
         self.__pyobjc_object__ = obj
         return self
 
+    __class__ = property(lambda self: self.__pyobjc_object__.__class__)
+
     def __getattr__(self, attr):
         return getattr(self.__pyobjc_object__, attr)
 
 class OC_PythonLong(long):
+
     def __new__(cls, obj, value):
         self = long.__new__(cls, value)
         self.__pyobjc_object__ = obj
         return self
 
+    __class__ = property(lambda self: self.__pyobjc_object__.__class__)
+
     def __getattr__(self, attr):
         return getattr(self.__pyobjc_object__, attr)
 
+    # The long type doesn't support __slots__ on subclasses, fake
+    # one part of the effect of __slots__: don't allow setting of attributes.
+    def __setattr__(self, attr, value):
+        if attr != '__pyobjc_object__':
+            raise AttributeError, "'%s' object has no attribute '%s')"%(self.__class__.__name__, attr)
+        self.__dict__['__pyobjc_object__'] = value
+
 class OC_PythonInt(int):
+    __slots__=('__pyobjc_object__',)
+
     def __new__(cls, obj, value):
         self = int.__new__(cls, value)
         self.__pyobjc_object__ = obj
         return self
 
+    __class__ = property(lambda self: self.__pyobjc_object__.__class__)
+
     def __getattr__(self, attr):
         return getattr(self.__pyobjc_object__, attr)
 
 
 def numberWrapper(obj):
     if isinstance(obj, NSDecimalNumber):
+        return obj
         # ensure that NSDecimal is around
         global Foundation
         if Foundation is None:
             import Foundation
         # return NSDecimal
-        return obj.decimalValue()
+        return Foundation.NSDecimal(obj)
     try:
         tp = obj.objCType()
     except AttributeError:

Lib/objc/test/test_identity.py

+# Some tests that verify that object identity is correctly retained 
+# accross the bridge
+# 
+# TODO:
+# - Add unittests here for every class that is treated specially by 
+#   the bridge.
+# - Add unittests that test for "real-world" scenarios (writing stuff
+#   to plists, ...)
+# - Implement the required functionality
+
+import unittest, sys, os
+import objc
+from objc.test.identity import *
+
+class TestPythonRoundTrip (unittest.TestCase):
+    # TODO: verify
+
+    def testBasicPython(self):
+
+        container = OC_TestIdentity.alloc().init()
+
+        for v in (self, object(), list, sum):
+            container.setStoredObject_(v)
+            self.assert_(v is container.storedObject(), repr(v))
+
+
+    def testPythonContainer(self):
+
+        container = OC_TestIdentity.alloc().init()
+
+        for v in ( [1, 2,3], (1,2,3), {1:2, 3:4} ):
+            container.setStoredObject_(v)
+            self.assert_(v is container.storedObject(), repr(v))
+
+    def dont_testPythonStrings(self):
+        # XXX: this test would always fail, this is by design.
+
+        container = OC_TestIdentity.alloc().init()
+
+        for v in ( u"Hello world", "Hello world" ):
+            container.setStoredObject_(v)
+            self.assert_(v is container.storedObject(), repr(v))
+
+    def dont_testPythonNumber(self):
+        # XXX: this test would always fail, need to move some code to C
+        # to fix (but not now)
+        container = OC_TestIdentity.alloc().init()
+
+        for v in (99999, 99999L, 10.0):
+            container.setStoredObject_(v)
+            self.assert_(v is container.storedObject, repr(v))
+
+
+class ObjCRoundTrip (unittest.TestCase):
+    # TODO: NSProxy
+
+    def testNSObject(self):
+        container = OC_TestIdentity.alloc().init()
+
+        cls = objc.lookUpClass("NSObject")
+        container.setStoredObjectToResultOf_on_("new", cls)
+
+        v = container.storedObject()
+        self.assert_(container.isSameObjectAsStored_(v), repr(v))
+
+        container.setStoredObjectToResultOf_on_("class", cls)
+        v = container.storedObject()
+        self.assert_(container.isSameObjectAsStored_(v), repr(v))
+
+    def testNSString(self):
+
+        container = OC_TestIdentity.alloc().init()
+
+        cls = objc.lookUpClass("NSObject")
+        container.setStoredObjectToResultOf_on_("description", cls)
+        v = container.storedObject()
+        self.assert_(container.isSameObjectAsStored_(v), repr(v))
+        self.assert_(isinstance(v, unicode))
+
+        cls = objc.lookUpClass("NSMutableString")
+        container.setStoredObjectToResultOf_on_("new", cls)
+        v = container.storedObject()
+        self.assert_(container.isSameObjectAsStored_(v), repr(v))
+        self.assert_(isinstance(v, unicode))
+
+    def testProtocol(self):
+        container = OC_TestIdentity.alloc().init()
+
+        container.setStoredObjectToAProtocol()
+        v = container.storedObject()
+        self.assert_(container.isSameObjectAsStored_(v), repr(v))
+        self.assert_(isinstance(v, objc.lookUpClass("Protocol")))
+
+    def testObject(self):
+        container = OC_TestIdentity.alloc().init()
+
+        cls = objc.lookUpClass("Object")
+        container.setStoredObjectAnInstanceOf_(cls)
+        v = container.storedObject()
+        self.assert_(container.isSameObjectAsStored_(v), repr(v))
+        self.assert_(isinstance(v, cls))
+
+    def testNSNumber(self):
+        container = OC_TestIdentity.alloc().init()
+
+        container.setStoredObjectToInteger_(10)
+        v = container.storedObject()
+        self.assert_(container.isSameObjectAsStored_(v), repr(v))
+
+        container.setStoredObjectToInteger_(-40)
+        v = container.storedObject()
+        self.assert_(container.isSameObjectAsStored_(v), repr(v))
+
+        container.setStoredObjectToUnsignedInteger_(40)
+        v = container.storedObject()
+        self.assert_(container.isSameObjectAsStored_(v), repr(v))
+
+        container.setStoredObjectToUnsignedInteger_(sys.maxint * 2)
+        v = container.storedObject()
+        self.assert_(container.isSameObjectAsStored_(v), repr(v))
+
+        container.setStoredObjectToLongLong_(sys.maxint * 2)
+        v = container.storedObject()
+        self.assert_(container.isSameObjectAsStored_(v), repr(v))
+
+        container.setStoredObjectToLongLong_(-sys.maxint)
+        v = container.storedObject()
+        self.assert_(container.isSameObjectAsStored_(v), repr(v))
+
+        container.setStoredObjectToUnsignedLongLong_(sys.maxint * 2)
+        v = container.storedObject()
+        self.assert_(container.isSameObjectAsStored_(v), repr(v))
+
+        container.setStoredObjectToFloat_(10.0)
+        v = container.storedObject()
+        self.assert_(container.isSameObjectAsStored_(v), repr(v))
+
+        container.setStoredObjectToDouble_(9999.0)
+        v = container.storedObject()
+        self.assert_(container.isSameObjectAsStored_(v), repr(v))
+
+    def testNSDecimalNumber(self):
+        container = OC_TestIdentity.alloc().init()
+        cls = objc.lookUpClass("NSDecimalNumber")
+        container.setStoredObjectToResultOf_on_("zero", cls)
+        v = container.storedObject()
+        self.assert_(container.isSameObjectAsStored_(v), repr(v))
+
+
+class ObjCtoPython (unittest.TestCase):
+    # TODO: NSProxy
+
+    def assertFetchingTwice(self, container, message = None):
+            v1 = container.storedObject()
+            v2 = container.storedObject()
+
+            self.assert_(v1 is v2, message)
+
+    def testNSObject(self):
+        container = OC_TestIdentity.alloc().init()
+
+        cls = objc.lookUpClass("NSObject")
+        container.setStoredObjectToResultOf_on_("new", cls)
+
+        self.assertFetchingTwice(container, repr(cls))
+
+        container.setStoredObjectToResultOf_on_("new", cls)
+        v1 = container.storedObject()
+        container.setStoredObjectToResultOf_on_("new", cls)
+        v2 = container.storedObject()
+        self.assert_(v1 is not v2, "different objects")
+
+    def dont_testNSString(self):
+        # This would always fail, NSStrings get a new proxy everytime
+        # they cross the bridge, otherwise it would be unnecessarily hard
+        # to get at the current value of NSMutableStrings.
+
+        container = OC_TestIdentity.alloc().init()
+
+        cls = objc.lookUpClass("NSObject")
+        container.setStoredObjectToResultOf_on_("description", cls)
+        self.assertFetchingTwice(container, "string")
+
+        cls = objc.lookUpClass("NSMutableString")
+        container.setStoredObjectToResultOf_on_("new", cls)
+        self.assertFetchingTwice(container, "mutable string")
+
+    def testProtocol(self):
+        container = OC_TestIdentity.alloc().init()
+
+        container.setStoredObjectToAProtocol()
+        v = container.storedObject()
+        self.assertFetchingTwice(container, "protocol")
+
+    def testObject(self):
+        container = OC_TestIdentity.alloc().init()
+
+        cls = objc.lookUpClass("Object")
+        container.setStoredObjectAnInstanceOf_(cls)
+        self.assertFetchingTwice(container, "object")
+
+    def dont_testNSNumber(self):
+        # No unique proxies yet, due to the way these proxies are
+        # implemented. This needs to be fixed, but that does not have
+        # a high priority.
+        container = OC_TestIdentity.alloc().init()
+
+        container.setStoredObjectToInteger_(10)
+        self.assertFetchingTwice(container, "int 10")
+
+        container.setStoredObjectToInteger_(-40)
+        self.assertFetchingTwice(container, "int -40")
+
+        container.setStoredObjectToUnsignedInteger_(40)
+        self.assertFetchingTwice(container, "unsigned int 40")
+
+        container.setStoredObjectToUnsignedInteger_(sys.maxint * 2)
+        self.assertFetchingTwice(container, "unsigned int sys.maxint*2")
+
+        container.setStoredObjectToLongLong_(sys.maxint * 2)
+        self.assertFetchingTwice(container, "long long sys.maxint*2")
+
+        container.setStoredObjectToLongLong_(-sys.maxint)
+        self.assertFetchingTwice(container, "long long -sys.maxint")
+
+        container.setStoredObjectToUnsignedLongLong_(sys.maxint * 2)
+        self.assertFetchingTwice(container, "unsigned long long sys.maxint*2")
+
+        container.setStoredObjectToFloat_(10.0)
+        self.assertFetchingTwice(container, "float")
+
+        container.setStoredObjectToDouble_(9999.0)
+        self.assertFetchingTwice(container, "double")
+
+    def testNSDecimalNumber(self):
+        container = OC_TestIdentity.alloc().init()
+
+        cls = objc.lookUpClass("NSDecimalNumber")
+        container.setStoredObjectToResultOf_on_("zero", cls)
+        print `container.storedObject()`, `type(container.storedObject())`
+        self.assertFetchingTwice(container, "decimal")
+
+class PythonToObjC (unittest.TestCase):
+    # TODO: verify
+
+    def testPlainObjects(self):
+        container = OC_TestIdentity.alloc().init()
+
+        for v in (self, object(), list, sum):
+            container.setStoredObject_(v)
+
+            self.assert_(container.isSameObjectAsStored_(v), repr(v))
+
+
+    def testContainers(self):
+        container = OC_TestIdentity.alloc().init()
+
+        for v in ([1,2,3], (1,2,3), {1:2, 3:4}):
+            container.setStoredObject_(v)
+
+            self.assert_(container.isSameObjectAsStored_(v), repr(v))
+
+    def dont_testStrings(self):
+        # These get converted, not proxied
+        container = OC_TestIdentity.alloc().init()
+
+        for v in ("hello world", u"a unicode string"):
+            container.setStoredObject_(v)
+
+            self.assert_(container.isSameObjectAsStored_(v), repr(v))
+
+    def dont_testNumbers(self):
+        # These get converted, not proxied
+        container = OC_TestIdentity.alloc().init()
+
+        for v in (1, 666666, 1L, 66666L, 1.0,):
+            container.setStoredObject_(v)
+
+            self.assert_(container.isSameObjectAsStored_(v), repr(v))
+
+class TestSerializingDataStructures (unittest.TestCase):
+    # OC_Python{Array,Dictionary} used to contain specialized 
+    # identity-preservation code. It is unclear why this was added, it might
+    # have to do with writing data to plist files.
+    # This TestCase tries to trigger the problem that caused the addition of
+    # said code, although unsuccesfully...
+
+    def tearDown(self):
+        if os.path.exists("/tmp/pyobjc.test.identity"):
+            os.unlink("/tmp/pyobjc.test.identity")
+
+    def testMakePlist(self):
+        container = OC_TestIdentity.alloc().init()
+
+        value = [ 1, 2, 3, [ "hello", ["world", ("in", 9 ) ], True, { "aap":3}]]
+        value.append(value[3])
+
+        container.setStoredObject_(value)
+        container.writeStoredObjecToFile_("/tmp/pyobjc.test.identity")
+
+        value = {
+            "hello": [ 1, 2, 3],
+            "world": {
+                "nl": "wereld",
+                "de": "Welt",
+            }
+        }
+        container.setStoredObject_(value)
+        container.writeStoredObjecToFile_("/tmp/pyobjc.test.identity")
+
+        
+
+if __name__ == "__main__":
+    unittest.main()

Modules/Foundation/decimals.m

 			return 0;
 			
 
+		} else if (PyObjCObject_Check(pyValue)) {
+			NSObject* value = PyObjC_PythonToId(pyValue);
+			if ([value isKindOfClass:[NSDecimalNumber class]]) {
+				((DecimalObject*)self)->value = [
+					(NSDecimalNumber*)value decimalValue
+				];
+
+				((DecimalObject*)self)->objc_value = 
+					(NSDecimalNumber*)value;
+				[value retain];
+				return 0;
+			}
+			PyErr_Format(PyExc_TypeError, "cannot convert object of %s to NSDecimal", pyValue->ob_type->tp_name);
+			return -1;
 		} else if (!PyString_Check(pyValue) && !PyUnicode_Check(pyValue)) {
 			PyErr_Format(PyExc_TypeError, "cannot convert object of %s to NSDecimal", pyValue->ob_type->tp_name);
 			return -1;
 
 	if (!Decimal_Check(*l)) {
 		/* The test is needed to avoid silently converting strings */
-		if (!(PyInt_Check(*l) || PyLong_Check(*l))) goto error;
+		if (PyString_Check(*l) || PyUnicode_Check(*l) || PyFloat_Check(*l)) goto error;
 		
 		left = (PyObject*)PyObject_New(DecimalObject, &Decimal_Type);
 		if (left == NULL) goto error;
 
 	if (!Decimal_Check(*r)) {
 		/* The test is needed to avoid silently converting strings */
-		if (!(PyInt_Check(*r) || PyLong_Check(*r))) goto error;
+		if (PyString_Check(*r) || PyUnicode_Check(*r) || PyFloat_Check(*r)) goto error;
 		
 		right = (PyObject*)PyObject_New(DecimalObject, &Decimal_Type);
 		if (right == NULL) goto error;

Modules/objc/OC_PythonArray.h

 @interface OC_PythonArray : NSMutableArray
 {
 	PyObject* value;
-	NSMapTable* table;
 }
 
 /*!

Modules/objc/OC_PythonArray.m

 #import "OC_PythonArray.h"
 
-static void
-nsmaptable_python_retain(NSMapTable *table __attribute__((__unused__)), const void *datum) {
-	Py_INCREF((PyObject *)datum);
-}
-
-static void
-nsmaptable_python_release(NSMapTable *table __attribute__((__unused__)), void *datum) {
-	Py_DECREF((PyObject *)datum);
-}
-
-static void
-nsmaptable_objc_retain(NSMapTable *table __attribute__((__unused__)), const void *datum) {
-	[(id)datum retain];
-}
-
-static void
-nsmaptable_objc_release(NSMapTable *table __attribute__((__unused__)), void *datum) {
-	[(id)datum release];
-}
-
-static
-NSMapTableKeyCallBacks PyObjC_ObjectToIdTable_KeyCallBacks = {
-	NULL, // use pointer value for hash
-	NULL, // use pointer value for equality
-	&nsmaptable_python_retain,
-	&nsmaptable_python_release,
-	NULL, // generic description
-	NULL // not a key
-};
-
-static
-NSMapTableValueCallBacks PyObjC_ObjectToIdTable_ValueCallBacks = {
-	&nsmaptable_objc_retain,
-	&nsmaptable_objc_release,
-	NULL  // generic description
-};
-
 @implementation OC_PythonArray 
 
 + newWithPythonObject:(PyObject*)v;
 	Py_INCREF(v);
 	Py_XDECREF(value);
 	value = v;
-	if (table) {
-		NSResetMapTable(table);
-	} else {
-		table = NSCreateMapTable(PyObjC_ObjectToIdTable_KeyCallBacks, PyObjC_ObjectToIdTable_ValueCallBacks, [self count]);
-	}
-	NSMapInsert(table, (const void *)Py_None, (const void *)[NSNull null]);
 	return self;
 }
 
 -(void)dealloc
 {
 	PyObjC_BEGIN_WITH_GIL
-		if (table) {
-			NSFreeMapTable(table);
-		}
+		PyObjC_UnregisterObjCProxy(value, self);
 		Py_XDECREF(value);
 
 	PyObjC_END_WITH_GIL
 			PyObjC_GIL_FORWARD_EXC();
 		}
 
-		if ((result = (id)NSMapGet(table, (const void *)v))) {
+		err = depythonify_c_value(@encode(id), v, &result);
+		if (err == -1) {
+			PyObjC_GIL_FORWARD_EXC();
+		} else {
 			Py_DECREF(v);
-		} else {
-			err = depythonify_c_value(@encode(id), v, &result);
-			if (err == -1) {
-				PyObjC_GIL_FORWARD_EXC();
-			} else {
-				NSMapInsert(table, (const void *)v, (const void *)result);
-				Py_DECREF(v);
-			}
 		}
 	
 	PyObjC_END_WITH_GIL

Modules/objc/OC_PythonDictionary.h

 @interface OC_PythonDictionary : NSMutableDictionary
 {
 	PyObject* value;
-    NSMapTable* table;
 }
 
 /*!

Modules/objc/OC_PythonDictionary.m

 #import "OC_PythonDictionary.h"
 
-static void
-nsmaptable_python_retain(NSMapTable *table __attribute__((__unused__)), const void *datum) {
-	Py_INCREF((PyObject *)datum);
-}
-
-static void
-nsmaptable_python_release(NSMapTable *table __attribute__((__unused__)), void *datum) {
-	Py_DECREF((PyObject *)datum);
-}
-
-static void
-nsmaptable_objc_retain(NSMapTable *table __attribute__((__unused__)), const void *datum) {
-	[(id)datum retain];
-}
-
-static void
-nsmaptable_objc_release(NSMapTable *table __attribute__((__unused__)), void *datum) {
-	[(id)datum release];
-}
-
-static
-NSMapTableKeyCallBacks PyObjC_ObjectToIdTable_KeyCallBacks = {
-	NULL, // use pointer value for hash
-	NULL, // use pointer value for equality
-	&nsmaptable_python_retain,
-	&nsmaptable_python_release,
-	NULL, // generic description
-	NULL // not a key
-};
-
-static
-NSMapTableValueCallBacks PyObjC_ObjectToIdTable_ValueCallBacks = {
-	&nsmaptable_objc_retain,
-	&nsmaptable_objc_release,
-	NULL  // generic description
-};
-
-
-
-
 /*
  * OC_PythonDictionaryEnumerator - Enumerator for Python dictionaries
  *
 	Py_INCREF(v);
 	Py_XDECREF(value);
 	value = v;
-	if (table) {
-		NSResetMapTable(table);
-	} else {
-		table = NSCreateMapTable(PyObjC_ObjectToIdTable_KeyCallBacks, PyObjC_ObjectToIdTable_ValueCallBacks, [self count]);
-	}
-	NSMapInsert(table, (const void *)Py_None, (const void *)[NSNull null]);
 	return self;
 }
 
 -(void)dealloc
 {
 	PyObjC_BEGIN_WITH_GIL
+		PyObjC_UnregisterObjCProxy(value, self);
 		Py_XDECREF(value);
-		if (table) {
-			NSFreeMapTable(table);
-		}
 	
 	PyObjC_END_WITH_GIL
 
 
 -(int)depythonify:(PyObject*)v toId:(id*)datum
 {
-	if (!(*datum = (id)NSMapGet(table, (const void *)v))) {
-		if (depythonify_c_value(@encode(id), v, datum) == -1) {
-			return -1;
-		}
-		NSMapInsert(table, (const void *)v, (const void *)*datum);
+	if (depythonify_c_value(@encode(id), v, datum) == -1) {
+		return -1;
 	}
 	return 0;
 }

Modules/objc/OC_PythonObject.m

 NSMapTable *PyObjC_ObjectToIdTable = NULL;
 #endif
 
+/* FIXME: PyObjC_RegisterObjCProxy should be moved to a central location! */
 @implementation OC_PythonObject
 + (int)wrapPyObject:(PyObject *)argument toId:(id *)datum
 {
 	int r;
 	id rval;
-    PyObject *anObject;
+	PyObject *anObject;
 	 
 	if (argument == Py_None) {
 		rval = nil;
 		r = 0;
 		goto end;
 	}
-#if 0
-	if (!PyObjC_ObjectToIdTable) {
-		PyObjC_ObjectToIdTable = NSCreateMapTable(PyObjC_ObjectToIdTable_KeyCallBacks, PyObjC_ObjectToIdTable_ValueCallBacks, 1024);
+
+	rval = PyObjC_FindObjCProxy(argument);
+	if (rval != nil) { 
+		r = 0; 
+		goto end; 
 	}
-	if ((*datum = (id)NSMapGet(PyObjC_ObjectToIdTable, argument))) {
-		// key found
-		return 0;
-	}
-#endif
 	
 	if (PyObjCClass_Check (argument)) {
 		rval = (id)PyObjCClass_GetClass(argument);
 	} else if (PyList_Check(argument) || PyTuple_Check(argument)) {
 		rval = [OC_PythonArray 
 			newWithPythonObject:argument];
+		PyObjC_RegisterObjCProxy(argument, rval);
 		r = 0;
 	} else if (PyDict_Check(argument)) {
 		rval = [OC_PythonDictionary 
 			newWithPythonObject:argument];
+		PyObjC_RegisterObjCProxy(argument, rval);
 		r = 0;
 #ifdef MACOSX
 	} else if ((rval = PyObjC_CFTypeToID(argument))) {
 		NS_DURING
 			rval = [OC_PythonObject 
 				newWithCoercedObject:argument];
+
 			r = 0;
 
 		NS_HANDLER
 
 		NS_ENDHANDLER
 	}
-#if 0
-	NSMapInsert(PyObjC_ObjectToIdTable, (const void *)argument, (const void *)rval);
-#endif
+
 end:
 	*datum = rval;
 	return r;
 - initWithObject:(PyObject *) obj
 {
 	PyObjC_BEGIN_WITH_GIL
+		if (pyObject) {
+			PyObjC_UnregisterObjCProxy(pyObject, self);
+		}
+		PyObjC_RegisterObjCProxy(obj, self);
 		Py_XINCREF(obj);
 		Py_XDECREF(pyObject);
 		pyObject = obj;
 - (void)dealloc
 {
 	PyObjC_BEGIN_WITH_GIL
+		PyObjC_UnregisterObjCProxy(pyObject, self);
 		Py_XDECREF(pyObject);
 
 	PyObjC_END_WITH_GIL

Modules/objc/alloc_hack.m

 	IMP anIMP;
 	Class aClass;
 	SEL volatile aSel;
+	PyObject* v;
 
 	if (PyArg_ParseTuple(arguments, "") < 0) {
 		return NULL;

Modules/objc/module.m

 
 	PyObjCClass_DefaultModule = PyString_FromString("objc");
 
-    /*
 	if (PyObjC_InitProxyRegistry() < 0) {
 		return;
 	}
-    */
 
 	PyType_Ready(&PyObjCClass_Type); 
 	PyType_Ready((PyTypeObject*)&PyObjCObject_Type);

Modules/objc/objc-object.m

 #endif  
         
 
-static NSMapTable* proxy_dict = NULL;
-
-static PyObject* 
-find_existing_proxy(id objc_obj)
-{
-	PyObject* v;
-
-	if (proxy_dict == NULL) return NULL;
-
-	v = NSMapGet(proxy_dict, objc_obj);
-	Py_XINCREF(v);
-	return v;
-}
-
-static void 
-unregister_proxy(id objc_obj)
-{
-	if (proxy_dict == NULL) return;
-	if (objc_obj == nil) return;
-
-	NSMapRemove(proxy_dict, objc_obj);
-}
-
-static int
-register_proxy(PyObject* proxy_obj) 
-{
-	id objc_obj;
-
-	if (PyObjCObject_Check(proxy_obj)) {
-		objc_obj = PyObjCObject_GetObject(proxy_obj);
-	} else if (PyObjCClass_Check(proxy_obj)) {
-		objc_obj = PyObjCClass_GetClass(proxy_obj);
-	} else if (PyObjCUnicode_Check(proxy_obj)) {
-		objc_obj = PyObjCUnicode_Extract(proxy_obj);
-	} else {
-		PyErr_SetString(PyExc_TypeError, 
-			"bad argument for register_proxy");
-		return -1;
-	}
-	assert(objc_obj != nil);
-
-	if (proxy_dict == NULL)  {
-		proxy_dict =  NSCreateMapTable(
-			PyObjCUtil_PointerKeyCallBacks,
-			PyObjCUtil_PointerValueCallBacks, 
-			500);
-
-		if (proxy_dict == NULL) {
-			PyErr_SetString(PyExc_RuntimeError,
-					"Cannot create NSMapTable");
-			return -1;
-		}
-	}
-
-	NSMapInsert(proxy_dict, objc_obj, proxy_obj);
-
-	return 0;
-}
-
-
-
 static PyObject*
 object_new(
 	PyTypeObject*  type __attribute__((__unused__)),
 		/* Release the proxied object, we don't have to do this when
 		 * there is no proxied object.
 		 */
-		unregister_proxy(PyObjCObject_GetObject(obj));
+		PyObjC_UnregisterPythonProxy(
+			PyObjCObject_GetObject(obj), obj);
 
 		if (PyObjCObject_IsClassic(obj)) {
 			/* pass */
 			[objc_object retain];
 		}
 
-		if (register_proxy(obj) < 0) {
-			NSLog(@"Couldn't register revived proxy object!");
-		}
 		return;
 	}
 	Py_DECREF(obj);
 	PyTypeObject* cls_type;
 	PyObject*     res;
 
-	res = find_existing_proxy(objc_object);
+	res = PyObjC_FindPythonProxy(objc_object);
 	if (res) return res;
 
 	if (objc_object == NULL) {
 		[objc_object retain];
 	}
 
-	if (register_proxy(res) < 0) {
-		Py_DECREF(res);
-		return NULL;
-	}
-
 	return res;
 }
 
 	PyTypeObject* cls_type;
 	PyObject*     res;
 
-	res = find_existing_proxy(objc_object);
+	res = PyObjC_FindPythonProxy(objc_object);
 	if (res) return res;
 
 	if (objc_object == NULL) {
 	((PyObjCObject*)res)->objc_object = objc_object;
 	((PyObjCObject*)res)->flags = PyObjCObject_kCLASSIC;
 
-	if (register_proxy(res) < 0) {
-		Py_DECREF(res);
-		return NULL;
-	}
-
 	return res;
 }
 
 	PyTypeObject* cls_type;
 	PyObject*     res;
 
-	res = find_existing_proxy(objc_object);
+	res = PyObjC_FindPythonProxy(objc_object);
 	if (res) return res;
 
 	if (objc_object == NULL) {
 	((PyObjCObject*)res)->objc_object = objc_object;
 	((PyObjCObject*)res)->flags = 0;
 
-	if (register_proxy(res) < 0) {
-		Py_DECREF(res);
-		return NULL;
-	}
+	PyObjC_RegisterPythonProxy(objc_object, res);
 
 	return res;
 }
 			object->ob_type->tp_name);
 		
 	}
-	unregister_proxy(((PyObjCObject*)object)->objc_object);
+	PyObjC_UnregisterPythonProxy(
+			((PyObjCObject*)object)->objc_object, object);
 	((PyObjCObject*)object)->objc_object = nil;
 }

Modules/objc/objc_support.m

 
 -(PyObject*)__pyobjc_PythonObject__
 {
-	PyObject *rval = (PyObject *)PyObjCObject_New(self);
+	PyObject *rval;
+
+	rval = PyObjC_FindPythonProxy(self);
+	if (rval == NULL) {
+		rval = (PyObject *)PyObjCObject_New(self);
+		PyObjC_RegisterPythonProxy(self, rval);
+	}
+
 	return rval;
 }
 
 +(PyObject*)__pyobjc_PythonObject__
 {
-	PyGILState_STATE state = PyGILState_Ensure();
-	PyObject *rval = (PyObject *)PyObjCClass_New(self);
+	PyObject *rval;
+
+	//rval = PyObjC_FindPythonProxy(self);
+	rval = NULL;
 	if (rval == NULL) {
-		PyObjCErr_ToObjCWithGILState(&state);
+		rval = (PyObject *)PyObjCClass_New(self);
+		//PyObjC_RegisterPythonProxy(self, rval);
 	}
-	PyGILState_Release(state);
+
 	return rval;
 }
 
 
 -(PyObject*)__pyobjc_PythonObject__
 {
-	PyObject *rval = (PyObject *)PyObjCObject_New(self);
+	PyObject *rval;
+
+	rval = PyObjC_FindPythonProxy(self);
+	if (rval == NULL) {
+		rval = (PyObject *)PyObjCObject_New(self);
+		PyObjC_RegisterPythonProxy(self, rval);
+	}
 	return rval;
 }
 
 +(PyObject*)__pyobjc_PythonObject__
 {
-	PyObject *rval = (PyObject *)PyObjCClass_New(self);
+	PyObject *rval;
+
+	rval = NULL;
+	//rval = PyObjC_FindPythonProxy(self);
+	if (rval == NULL) {
+		rval = (PyObject *)PyObjCClass_New(self);
+		//PyObjC_RegisterPythonProxy(self, rval);
+	}
 	return rval;
 }
 
 
 -(PyObject*)__pyobjc_PythonObject__
 {
-	PyObject* rval =  (PyObject *)PyObjCObject_NewClassic(self);
+	PyObject *rval;
+
+	rval = PyObjC_FindPythonProxy(self);
+	if (rval == NULL) {
+		rval = (PyObject *)PyObjCObject_NewClassic(self);
+		PyObjC_RegisterPythonProxy(self, rval);
+	}
 	return rval;
 }
 
 
 -(PyObject*)__pyobjc_PythonObject__
 {
-	PyObject* rval =  (PyObject *)PyObjCObject_NewClassic(self);
+	PyObject *rval;
+
+	rval = PyObjC_FindPythonProxy(self);
+	if (rval == NULL) {
+		rval = (PyObject *)PyObjCObject_NewClassic(self);
+		PyObjC_RegisterPythonProxy(self, rval);
+	}
 	return rval;
 }
 
 
 -(PyObject*)__pyobjc_PythonObject__
 {
+	/* Don't register the proxy, see XXX */
 	PyObject *rval = (PyObject *)PyObjCUnicode_New(self);
 	return rval;
 }
 @implementation NSNumber (PyObjCSupport)
 -(PyObject*)__pyobjc_PythonObject__
 {
-	PyObject *rval = [super __pyobjc_PythonObject__];
-	if (PyObjC_NSNumberWrapper && rval) {
-		PyObject *val = rval;
-		rval = PyObject_CallFunctionObjArgs(PyObjC_NSNumberWrapper, val, NULL);
-		Py_DECREF(val);
+	/* FIXME: rewrite PyObjC_NSNumberWrapper in C */
+	PyObject *rval;
+
+
+#ifdef MACOSX
+	/* shortcut for booleans */
+	if (kCFBooleanTrue == (CFBooleanRef)self) {
+		return PyBool_FromLong(1);
+	} else if (kCFBooleanFalse == (CFBooleanRef)self) {
+		return PyBool_FromLong(0);
 	}
+#endif
+	
+	rval = PyObjC_FindPythonProxy(self);
+	if (rval == NULL) {
+		rval= PyObjCObject_New(self);
+
+		if (PyObjC_NSNumberWrapper && rval) {
+			PyObject *val = rval;
+			rval = PyObject_CallFunctionObjArgs(
+					PyObjC_NSNumberWrapper, val, NULL);
+			Py_DECREF(val);
+		}
+	}
+	return rval;
+}
+@end
+
+@interface NSDecimalNumber (PyObjCSupport)
+-(PyObject*)__pyobjc_PythonObject__;
+@end /* NSDecimalNumber (PyObjCSupport) */
+
+@implementation NSDecimalNumber (PyObjCSupport)
+-(PyObject*)__pyobjc_PythonObject__
+{
+	PyObject *rval;
+
+	rval = PyObjC_FindPythonProxy(self);
+	if (rval == NULL) {
+		rval = (PyObject *)PyObjCObject_New(self);
+		PyObjC_RegisterPythonProxy(self, rval);
+	}
+
 	return rval;
 }
 @end
 
 		if (obj == nil) {
 			retobject = Py_None;
-				Py_INCREF (retobject);
+			Py_INCREF (retobject);
 		} else {
 			retobject = [obj  __pyobjc_PythonObject__];
 		}

Modules/objc/pyobjc.h

 #define PyObjC_UNICODE_FAST_PATH
 #endif
 
+#include "proxy-registry.h"
 #include "objc_support.h"
 #include "pointer-support.h"
 #include "OC_PythonObject.h"

Modules/objc/test/identity.m

+/*
+ * This module is used in the unittests for object identity.
+ */
+#include "Python.h"
+#include "pyobjc-api.h"
+
+#import <Foundation/Foundation.h>
+
+@interface OC_TestIdentity : NSObject
+{
+	NSObject* storedObject;
+}
+
+-(NSObject*)storedObject;
+-(void)setStoredObject:(NSObject*)object;
+-(void)dealloc;
+
+-(void)setStoredObjectToResultOf:(SEL)aSelector on:(NSObject*)object;
+-(void)setStoredObjectToInteger:(int)value;
+-(void)setStoredObjectToUnsignedInteger:(unsigned int)value;
+-(void)setStoredObjectToLongLong:(long long)value;
+-(void)setStoredObjectToUnsignedLongLong:(unsigned long long)value;
+-(void)setStoredObjectToDouble:(double)value;
+-(void)setStoredObjectToFloat:(float)value;
+
+-(int)isSameObjectAsStored:(NSObject*)value;
+-(void)setStoredObjectToAProtocol;
+-(void)setStoredObjectAnInstanceOf:(Class) cls;
+
+-(void)writeStoredObjecToFile:(NSString*)fname;
+
+@end
+
+@implementation OC_TestIdentity
+-(void)dealloc
+{
+	[storedObject release];
+}
+
+-(NSObject*)storedObject
+{
+	return [[storedObject retain] autorelease];
+}
+
+-(void)setStoredObject:(NSObject*)object
+{
+	[object retain];
+	[storedObject release];
+	storedObject = object;
+}
+
+-(void)setStoredObjectToResultOf:(SEL)aSelector on:(NSObject*)object
+{
+	[self setStoredObject: [object performSelector: aSelector]];
+}
+
+-(void)setStoredObjectToInteger:(int)value
+{
+	[self setStoredObject: [NSNumber numberWithInt: value]];
+}
+
+-(void)setStoredObjectToUnsignedInteger:(unsigned int)value
+{
+	[self setStoredObject: [NSNumber numberWithUnsignedInt: value]];
+}
+
+-(void)setStoredObjectToLongLong:(long long)value
+{
+	[self setStoredObject: [NSNumber numberWithLongLong: value]];
+}
+
+-(void)setStoredObjectToUnsignedLongLong:(unsigned long long)value
+{
+	[self setStoredObject: [NSNumber numberWithUnsignedLongLong: value]];
+}
+
+-(void)setStoredObjectToDouble:(double)value
+{
+	[self setStoredObject: [NSNumber numberWithDouble: value]];
+}
+
+-(void)setStoredObjectToFloat:(float)value
+{
+	[self setStoredObject: [NSNumber numberWithFloat: value]];
+}
+
+-(int)isSameObjectAsStored:(NSObject*)value
+{
+	return value == storedObject;
+}
+
+-(void)setStoredObjectToAProtocol
+{
+	[self setStoredObject: (NSObject*)@protocol(NSObject) ];
+}
+
+-(void)setStoredObjectAnInstanceOf:(Class) cls
+{
+	[self setStoredObject: [[cls alloc] init]];
+}
+
+-(void)writeStoredObjecToFile:(NSString*)fname
+{
+	[(NSArray*)storedObject writeToFile:fname atomically:YES];
+}
+
+@end
+
+static PyMethodDef identity_methods[] = {
+	{ NULL, NULL, NULL, NULL }
+};
+
+void initidentity(void);
+void initidentity(void)
+{
+	PyObject* m;
+
+	m = Py_InitModule4("identity", identity_methods, 
+			NULL, NULL, PYTHON_API_VERSION);
+
+	PyObjC_ImportAPI(m);
+	PyModule_AddObject(m, "OC_TestIdentity", 
+		PyObjCClass_New([OC_TestIdentity class]));
+
+}

Modules/objc/unicode-object.m

 	PyObject* weakrefs = uobj->weakrefs;
 	PyObject* py_nsstr = uobj->py_nsstr;
 
+	PyObjC_UnregisterPythonProxy(nsstr, obj);
+
 	Py_XDECREF(py_nsstr);
 	[nsstr release];
 
 	return uobj->py_nsstr;
 }
 
+
 static PyObject*
 meth_getattro(PyObject *o, PyObject *attr_name)
 {
 	return meth_nsstring(self);
 }
 
-static PyGetSetDef nsstring_getseters[] = {
+static PyGetSetDef nsstring_getsetters[] = {
 	{
 		"__pyobjc_object__",
 		(getter)nsstring_get__pyobjc_object__, NULL,
 	0,					/* tp_iternext */
 	class_methods,				/* tp_methods */
 	0,					/* tp_members */
-	nsstring_getseters,	/* tp_getset */
+	nsstring_getsetters,			/* tp_getset */
 	&PyUnicode_Type,			/* tp_base */
 	0,					/* tp_dict */
 	0,					/* tp_descr_get */
 Version 1.3 (2005-03-??)
 ------------------------
 
+- The bridge now maintains object identity across the bridge
+  in both directions. Previous versions of the bridge only did this when
+  bridging from Objective-C to Python.
+
+  Exceptions: NSString and NSNumber do not have unique proxies. NSString
+  never will have. Python numbers and strings are converted, not proxied and
+  therefore also don't get unique proxies.
+
+  And finally, any python object that is proxied using the ``__pyobjc_object__``
+  interface will only get a unique proxy if the ``__pyobjc_object__`` method
+  implements that feature.
+
 - (UNFINISHED) New RemotePyInterpreter example that demonstrates an
   out-of-process Python interpreter (primarily for IDE uses).
 
   as the wrapped object.
 
 - ``NSNumber`` instances are bridged to a ``float``, ``long``, or ``int``
-  subclass that uses ``__pyobjc_object__``.  ``NSDecimalNumber`` is bridged
-  to ``Foundation.NSDecimal``, which now supports ``__pyobjc_object__``.
-  This eliminates a HUGE amount of cruft in ``objc._conveniences``.
+  subclass that uses ``__pyobjc_object__``.  
+  ``NSDecimal`` is converted to ``NSDecimalNumber`` when used as an object,
+  ``NSDecimalNumber`` is not bridged to ``NSDecimal`` because the latter is
+  a mutable type.
 
 - The Python to Objective-C bridge now looks for a ``__pyobjc_object__`` 
   attribute to get a PyObjC object from a Python object.

setup-lib/di_build_ext.py

         # the extensions.
         compiler_saved = self.compiler
 
-        runtasks("Generating wrappers & stubs",
-            [sys.executable, "Scripts/CodeGenerators/cocoa_generator.py"])
+        #runtasks("Generating wrappers & stubs",
+        #    [sys.executable, "Scripts/CodeGenerators/cocoa_generator.py"])
 
         if not os.path.exists('build/codegen/_Fnd_Classes.inc'):
             # Create a dummy classname list, to enable bootstrapping. Don't
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.