Commits

Bob Ippolito  committed 0cc3070

FIX: [ 1107823 ] Key-Value Observing doesn't work for 'manual' properties

Key-Value Coding of Python objects should act like Objective-C now.
Previously, its capitalization method didn't match Objective-C's, it
wouldn't use setter accessors with an underscore postfix, and it
generally didn't behave as documented.

  • Participants
  • Parent commits 8777035
  • Branches pyobjc-ancient

Comments (0)

Files changed (3)

File Lib/PyObjCTools/KeyValueCoding.py

     SETVALUEFORKEY = 'takeValue_forKey_'
     SETVALUEFORKEYPATH = 'takeValue_forKeyPath_'
 
+def keyCaps(s):
+    return s[:1].capitalize() + s[1:]
+
 def getKey(obj, key):
     """
     Get the attribute referenced by 'key'. The key is used
     The following attributes and accesors at tried (in this order):
     - Accessor 'getKey'
     - Accesoor 'get_key'
-    - Attribute or accessor 'key'
+    - Accessor or attribute 'key'
+    - Accessor or attribute 'isKey'
     - Attribute '_key'
 
     If none of these exist, raise KeyError
 
 
     try:
-        m = getattr(obj, "get" + key.capitalize())
+        m = getattr(obj, "get" + keyCaps(key))
     except AttributeError:
         pass
     else:
     else:
         return m()
 
-    try:
-        m = getattr(obj, key)
+    for keyName in (key, "is" + keyCaps(key)):
+        try:
+            m = getattr(obj, keyName)
+        except AttributeError:
+            continue
 
-        if isinstance(m, types.MethodType):
-            if m.im_self is obj:
-                return m()
-            else:
-                return m
+        if isinstance(m, types.MethodType) and m.im_self is obj:
+            return m()
 
         elif isinstance(m, types.BuiltinMethodType):
             # Can't access the bound self of methods of builtin classes :-(
             return m()
 
-        elif isinstance(m, objc.selector):
-            if m.self is obj:
-                return m()
-            else:
-                return m
+        elif isinstance(m, objc.selector) and m.self is obj:
+            return m()
+
         else:
             return m
 
+    try:
+        return getattr(obj, "_" + key)
     except AttributeError:
-        pass
+        raise KeyError, "Key %s does not exist" % (key,)
 
-    try:
-        m = getattr(obj, "_" + key)
-        if isinstance(m, types.MethodType):
-            return m()
-        else:
-            return m
-    except AttributeError:
-        pass
-
-    raise KeyError, "Key %s does not exist"%(key,)
 
 def setKey(obj, key, value):
     """
     to build the name of an attribute, or attribute accessor method.
 
     The following attributes and accessors are tried (in this order):
+    - Accessor 'setKey_'
     - Accessor 'setKey'
     - Accessor 'set_key'
     - Attribute '_key'
     """
     if isinstance(obj, (objc.objc_object, objc.objc_class)):
         try:
-            return getattr(obj, SETVALUEFORKEY)(value, key)
+            getattr(obj, SETVALUEFORKEY)(value, key)
+            return
         except ValueError, msg:
             raise KeyError, str(msg)
 
-    try:
-        m = getattr(obj, 'set' + key.capitalize())
-    except AttributeError:
-        pass
-    else:
-        m(value)
-        return
-
-    try:
-        m = getattr(obj, 'set_' + key)
-    except AttributeError:
-        pass
-    else:
-        m(value)
-        return
+    aBase = 'set' + keyCaps(key)
+    for accessor in (aBase + '_', aBase, 'set_' + key):
+        m = getattr(obj, accessor, None)
+        if m is None:
+            continue
+        try:
+            m(value)
+            return
+        except TypeError:
+            pass
 
     try:
         o = getattr(obj, "_" + key)
     try:
         setattr(obj, key, value)
     except AttributeError:
-        raise KeyError, "Key %s does not exist"%(key,)
+        raise KeyError, "Key %s does not exist" % (key,)
 
 def getKeyPath(obj, keypath):
     """
 <p>An overview of the relevant changes in new, and older, releases.</p>
 <h2><a name="version-1-3-1-2005-04">Version 1.3.1 (2005-04-??)</a></h2>
 <ul>
+<li>Key-Value Coding of Python objects should act like Objective-C now.
+Previously, its capitalization method didn't match Objective-C's, it
+wouldn't use setter accessors with an underscore postfix, and it
+generally didn't behave as documented.</li>
 <li>The formal protocol list should be more complete.  A new 
 <code><span>objc.protocolsForProcess()</span></code> enumerates over all mach
 headers and returns all of the protocols defined in the expected
 Version 1.3.1 (2005-04-??)
 --------------------------
 
+- Key-Value Coding of Python objects should act like Objective-C now.
+  Previously, its capitalization method didn't match Objective-C's, it
+  wouldn't use setter accessors with an underscore postfix, and it
+  generally didn't behave as documented.
+
 - The formal protocol list should be more complete.  A new 
   ``objc.protocolsForProcess()`` enumerates over all mach
   headers and returns all of the protocols defined in the expected