[PyObjC-svn] r2390 - in trunk/pyobjc/pyobjc-core: . Doc Doc/lib Lib/objc Modules/objc Modules/objc/test PyObjCTest

[email protected] Thu, 28 Jan 2010 04:44:28 -0600
Newsgroups gmane.comp.python.pyobjc.cvs
Message-ID <[email protected]>
Author: ronaldoussoren
Date: Thu Jan 28 04:44:28 2010
New Revision: 2390

Log:
Initial support for Objective-C style properties.

This adds a property class: objc.object_property 

This also adds a lot of machinery to allow writing
that class in Python, in particular hooks that
get called during class setup and a way to define
hidden methods.

Open issues:
* The unittests for this are basicly just stubbed out

* Hidden selector support needs more work.

* Documentation for this feature sucks



Added:
   trunk/pyobjc/pyobjc-core/Doc/index.rst
   trunk/pyobjc/pyobjc-core/Lib/objc/_properties.py   (contents, props changed)
   trunk/pyobjc/pyobjc-core/Modules/objc/test/properties.m
   trunk/pyobjc/pyobjc-core/PyObjCTest/test_propertiesforclass.py   (contents, props changed)
Modified:
   trunk/pyobjc/pyobjc-core/Doc/lib/module-objc.rst
   trunk/pyobjc/pyobjc-core/Lib/objc/__init__.py
   trunk/pyobjc/pyobjc-core/Lib/objc/_compat.py
   trunk/pyobjc/pyobjc-core/Lib/objc/_convenience.py
   trunk/pyobjc/pyobjc-core/Lib/objc/_descriptors.py
   trunk/pyobjc/pyobjc-core/Lib/objc/_functions.py
   trunk/pyobjc/pyobjc-core/Lib/objc/_pycoder.py
   trunk/pyobjc/pyobjc-core/Modules/objc/class-builder.h
   trunk/pyobjc/pyobjc-core/Modules/objc/class-builder.m
   trunk/pyobjc/pyobjc-core/Modules/objc/instance-var.m
   trunk/pyobjc/pyobjc-core/Modules/objc/libffi_support.m
   trunk/pyobjc/pyobjc-core/Modules/objc/module.m
   trunk/pyobjc/pyobjc-core/Modules/objc/objc-class.h
   trunk/pyobjc/pyobjc-core/Modules/objc/objc-class.m
   trunk/pyobjc/pyobjc-core/Modules/objc/pyobjc.h
   trunk/pyobjc/pyobjc-core/Modules/objc/selector.h
   trunk/pyobjc/pyobjc-core/Modules/objc/selector.m
   trunk/pyobjc/pyobjc-core/NEWS.txt
   trunk/pyobjc/pyobjc-core/setup.py

Modified: trunk/pyobjc/pyobjc-core/Doc/lib/module-objc.rst
==============================================================================
--- trunk/pyobjc/pyobjc-core/Doc/lib/module-objc.rst	(original)
+++ trunk/pyobjc/pyobjc-core/Doc/lib/module-objc.rst	Thu Jan 28 04:44:28 2010
@@ -106,6 +106,66 @@
 
    TODO
 
+
+.. function:: propertiesForClass(objcClass)
+
+   :type objcClass: an Objective-C class or formal protocol
+   :return: a list of properties from the Objective-C runtime
+
+   The return value is a list with information about
+   properties on this class or protocol from the Objective-C runtime. This
+   does not include properties superclasses.
+
+   Every entry in the list is dictionary with the following keys:
+
+   =============== =============================================================
+   Key             Description
+   =============== =============================================================
+   ``name``        Name of the property (a string)
+   --------------- -------------------------------------------------------------
+   ``raw_attr``    Raw value of the attribute string (a byte string)
+   --------------- -------------------------------------------------------------
+   ``typestr``     The type string for this attribute (a byte string)
+   --------------- -------------------------------------------------------------
+   ``classname``   When the type string is ``objc._C_ID`` this is the
+                   name of the Objective-C class (a string).
+   --------------- -------------------------------------------------------------
+   ``readonly``    True iff the property is read-only (bool)
+   --------------- -------------------------------------------------------------
+   ``copy``        True iff the property is copying the value (bool)
+   --------------- -------------------------------------------------------------
+   ``retain``      True iff the property is retaining the value (bool)
+   --------------- -------------------------------------------------------------
+   ``nonatomic``   True iff the property is not atomic (bool)
+   --------------- -------------------------------------------------------------
+   ``dynamic``     True iff the property is dynamic (bool)
+   --------------- -------------------------------------------------------------
+   ``weak``        True iff the property is weak (bool)
+   --------------- -------------------------------------------------------------
+   ``collectable`` True iff the property is collectable (bool)
+   --------------- -------------------------------------------------------------
+   ``getter``      Non-standard selector for the getter method (a byte string)
+   --------------- -------------------------------------------------------------
+   ``setter``      Non-standard selector for the setter method (a byte string)
+   =============== =============================================================
+
+   All values but ``name`` and ``raw_attr`` are optional. The other attributes
+   contain a decoded version of the ``raw_attr`` value. The boolean attributes
+   should be interpreted as ``False`` when the aren't present.
+
+   The documentation for the Objective-C runtime contains more information about
+   property definitions.
+
+   This function only returns information about properties as they are defined
+   in the Objective-C runtime, that is using ``@property`` definitions in an
+   Objective-C interface. Not all properties as they are commonly used  in
+   Objective-C are defined using that syntax, especially properties in classes
+   that were introduced before MacOSX 10.5.
+
+   This function always returns an empty list on MacOS X 10.4.
+
+   .. versionadded:: 2.3
+
 .. function:: listInstanceVariables
 
    TODO
@@ -280,6 +340,29 @@
 
    TODO
 
+.. function:: _setClassSetUpHook
+
+   This is a private hook that is called during the creation of a subclass.
+
+   WARNING: This hook is not part of the stable API.
+
+   .. versionadded:: 2.3
+
+.. function:: _setClassExtender
+
+   This is a private hook that's called during the creation of the proxy for
+   an Objective-C class.
+
+   WARNING: This hook is not part of the stable API.
+   
+   .. versionadded:: 2.2
+
+   .. versionchanged:: 2.3
+      TODO: In version 2.2 the hook gets called any time the bridge rescans
+      a class, in 2.3 the hook only gets called during initial construction
+      and has less oportunity to change things.
+    
+
 Types
 -----
 
@@ -712,3 +795,82 @@
 
 Due to technical details it is not possible to pickle an Objective-C object,
 unless someone explicitly implements the pickle protocol for such an object.
+
+Properties
+----------
+
+Introduction
+............
+
+Both Python and Objective-C have support for properties, which are object attributes
+that are accessed using attribute access syntax but which result in a method call.
+
+The Python built-in :class:`property <__builtin__.property__` is used to define new
+properties in plain Python code. These properties don't full interoperate with 
+Objective-C code though because they do not necessarily implement the Objective-C
+methods that mechanisms like Key-Value Coding use to interact with a class.
+
+PyObjC therefore has a number of property classes that allow you to define new
+properties that do interact fully with the Key-Value Coding and Observation
+frameworks.
+
+TODO: Implement method for enabling properties on existing classes and tell
+why that is off by default and when it will be turned on by default.
+
+TODO: The description is way to minimal, even the design document contained
+more information.
+
+.. class:: object_property(name=None, read_only=False, copy=False, dynamic=False, ivar=None, typestr=_C_ID, depends_on=None)
+
+
+   :param name: Name of the property, the default is to extract the name from the class dictionary
+   :param read_only: Is this a read-only property? The default is a read-write property.
+   :param copy: Should the default setter method copy values? The default retains the new value without copying.
+   :param dynamic: If this argument is ``True`` the property will not generate default accessor, 
+   but will rely on some external process to create them.
+   :param ivar: Name of the instance variable that's used to store the value. When this value is ``None``
+   the name will be calculated from the property name. If it is ``NULL`` there will be no instance variable.
+   :param typestr: The Objective-C type for this property, defaults to an arbitrary object.
+   :param depends_on: A sequence of names of properties the value of this property depends on.
+
+During the class definition you can add accessor methods by using the property as a decorator
+
+
+.. method:: object_property.getter
+
+   Decorator for defining the getter method for a property. The name of the method should be the
+   same as the property::
+
+       class MyObject (NSObject):
+
+           prop = objc.object_property()
+
+           @prop.getter
+           def prop(self):
+              return 42
+
+
+.. method:: object_property.setter
+
+   Decorator for defining the setter method for a property. The name of the method should be the
+   same as the property.
+
+
+.. method:: object_property.validate
+
+   Decorator for defining a Key-Value Coding validator for this property. 
+
+  
+It is possible to override property accessor in a subclass::
+
+   class MySubclass (MyObject):
+       @MyObject.prop.getter
+       def getter(self):
+           return "the world"
+
+This can also be used to convert a read-only property to a read-write one
+by adding a setter accessor.
+
+
+   
+   

Modified: trunk/pyobjc/pyobjc-core/Lib/objc/__init__.py
==============================================================================
--- trunk/pyobjc/pyobjc-core/Lib/objc/__init__.py	(original)
+++ trunk/pyobjc/pyobjc-core/Lib/objc/__init__.py	Thu Jan 28 04:44:28 2010
@@ -24,6 +24,8 @@
 _update()
 del _update
 
+#import objc._setup
+
 from objc._convenience import *
 from objc._bridgesupport import *
 
@@ -37,6 +39,7 @@
 from objc._functions import *
 from objc._locking import *
 from objc._context import *
+from objc._properties import *
 
 import objc._pycoder as _pycoder
 

Modified: trunk/pyobjc/pyobjc-core/Lib/objc/_compat.py
==============================================================================
--- trunk/pyobjc/pyobjc-core/Lib/objc/_compat.py	(original)
+++ trunk/pyobjc/pyobjc-core/Lib/objc/_compat.py	Thu Jan 28 04:44:28 2010
@@ -1,4 +1,5 @@
 __all__ = ['runtime', 'pluginBundle', 'registerPlugin']
+import warnings
 
 class Runtime:
     """
@@ -8,7 +9,6 @@
     older versions of PyObjC.
     """
     def __getattr__(self, name):
-        import warnings
         warnings.warn("Deprecated: use objc.lookUpClass",
             DeprecationWarning)
         import objc
@@ -37,6 +37,7 @@
 
     Register the current py2app plugin by name and return its bundle
     """
+    warnings.warn("Deprecated: use objc.currentBundle()", DeprecationWarning)
     import os
     import sys
     path = os.path.dirname(os.path.dirname(os.environ['RESOURCEPATH']))
@@ -52,7 +53,6 @@
     Return the main bundle for the named plugin. This should be used
     only after it has been registered with registerPlugin
     """
-    import warnings
     warnings.warn("Deprecated: use currentBundle()", DeprecationWarning)
     from Foundation import NSBundle
     return NSBundle.bundleWithPath_(_PLUGINS[pluginName])

Modified: trunk/pyobjc/pyobjc-core/Lib/objc/_convenience.py
==============================================================================
--- trunk/pyobjc/pyobjc-core/Lib/objc/_convenience.py	(original)
+++ trunk/pyobjc/pyobjc-core/Lib/objc/_convenience.py	Thu Jan 28 04:44:28 2010
@@ -17,7 +17,7 @@
     set(['__cmp__'])
 
 """
-from objc._objc import setClassExtender, selector, lookUpClass, currentBundle, repythonify, splitSignature, _block_call
+from objc._objc import _setClassExtender, selector, lookUpClass, currentBundle, repythonify, splitSignature, _block_call
 from itertools import imap
 import sys
 
@@ -153,7 +153,7 @@
 
         type_dict['_'] = property(kvc)
 
-setClassExtender(add_convenience_methods)
+_setClassExtender(add_convenience_methods)
 
 
 #

Modified: trunk/pyobjc/pyobjc-core/Lib/objc/_descriptors.py
==============================================================================
--- trunk/pyobjc/pyobjc-core/Lib/objc/_descriptors.py	(original)
+++ trunk/pyobjc/pyobjc-core/Lib/objc/_descriptors.py	Thu Jan 28 04:44:28 2010
@@ -91,11 +91,11 @@
         return selector(func, signature=signature)
     return _typedSelector
 
-def namedselector(name, signature=None):
+def namedSelector(name, signature=None):
     """
     Python 2.4 decorator for overriding the Objective-C SEL for a method, usage:
 
-        @namedselector("foo:bar:")
+        @namedSelector("foo:bar:")
         def foobar(self, foo, bar):
             return foo + bar
     """
@@ -108,6 +108,11 @@
 
     return _namedselector
 
+def namedselector(name, signature=None):
+    import warnings
+    warnings.warn("use objc.namedSelector instead of objc.namedselector")
+    return namedSelector(name, signature)
+
 def typedAccessor(typeSignature):
     """
     Python 2.4 decorator for creating a typed accessor, usage:

Modified: trunk/pyobjc/pyobjc-core/Lib/objc/_functions.py
==============================================================================
--- trunk/pyobjc/pyobjc-core/Lib/objc/_functions.py	(original)
+++ trunk/pyobjc/pyobjc-core/Lib/objc/_functions.py	Thu Jan 28 04:44:28 2010
@@ -1,31 +1,8 @@
-__all__ = ['inject', 'signature']
+__all__ = [ 'signature']
 
 import os
 import sys
 
-def _ensure_path(p):
-    p = os.path.realpath(p)
-    if isinstance(p, unicode):
-        p = p.encode(sys.getfilesystemencoding())
-    return p
-
-def inject(pid, bundle, useMainThread=True):
-    """Loads the given MH_BUNDLE in the target process identified by pid"""
-    try:
-        from objc._objc import _inject
-        from objc._dyld import dyld_find
-    except ImportError:
-        raise NotImplementedError("objc.inject is only supported on Mac OS X 10.3 and later")
-    bundlePath = bundle
-    systemPath = dyld_find('/usr/lib/libSystem.dylib')
-    carbonPath = dyld_find('/System/Library/Frameworks/Carbon.framework/Carbon')
-    paths = map(_ensure_path, (bundlePath, systemPath, carbonPath))
-    return _inject(
-        pid,
-        useMainThread,
-        *paths
-    )
-
 def signature(signature, **kw):
     """
     A Python method decorator that allows easy specification
@@ -37,6 +14,8 @@
         def methodWithX_andY_(self, x, y):
             return 0
     """
+    import warnings
+    warnings.warn("Usage objc.typedSelector instead of objc.signature")
     from objc._objc import selector
     kw['signature'] = signature
     def makeSignature(func):

Modified: trunk/pyobjc/pyobjc-core/Lib/objc/_pycoder.py
==============================================================================
--- trunk/pyobjc/pyobjc-core/Lib/objc/_pycoder.py	(original)
+++ trunk/pyobjc/pyobjc-core/Lib/objc/_pycoder.py	Thu Jan 28 04:44:28 2010
@@ -323,11 +323,11 @@
                 value.__dict__.update(state)
             except RuntimeError:
                 for k, v in state.items():
-                    setattr(value, k, v)
+                    setattr(value, intern(k), v)
 
         if slotstate:
             for k, v in slotstate.items():
-                setattr(value, k, v)
+                setattr(value, intern(k), v)
 
         return value
     decode_dispatch[kOP_INST] = load_inst
@@ -363,11 +363,11 @@
 
             except RuntimeError:
                 for k, v in state.items():
-                    setattr(value, k, v)
+                    setattr(value, intern(k), v)
 
         if slotstate:
             for k, v in slotstate.items():
-                setattr(value, k, v)
+                setattr(value, intern(k), v)
 
         if listitems:
             for a in listitems:

Modified: trunk/pyobjc/pyobjc-core/Modules/objc/class-builder.h
==============================================================================
--- trunk/pyobjc/pyobjc-core/Modules/objc/class-builder.h	(original)
+++ trunk/pyobjc/pyobjc-core/Modules/objc/class-builder.h	Thu Jan 28 04:44:28 2010
@@ -1,78 +1,18 @@
 #ifndef OBJC_CLASS_BUILDER
 #define OBJC_CLASS_BUILDER
 
-/*!
- * @header class-builder.h
- * @abstract Module for creating Objective-C classes
- * @discussion
- *    This module defines the functions that are used to create new
- *    classes in the Objective-C runtime for subclasses of NSObject.
- *
- *    The protocol for building a new class:
- *    1) Collect the necessary information (name, bases and class_dict)
- *    2) Call PyObjCClass_BuildClass
- *    3) Create the Python class (using type.__new__)
- *    4) Call PyObjCClass_FinishClass
- *
- *    If step 3 fails: call PyObjCClass_UnbuildClass
- *
- *    Note that it is not possible to remove classes from the Objective-C 
- *    runtime (at least with the Apple runtime, not sure about the GNU runtime).
- */
+extern PyObject* PyObjC_class_setup_hook;
 
-/*!
- * @function PyObjCClass_BuildClass
- * @abstract Create a new Objective-C class, but do not register it
- * @param super_class The super class for the new class
- * @param protocols   The list of protocols that the class conforms to
- * @param name        Name of the class
- * @param class_dict  The __dict__ for the new class
- * @result  Returns the newly created class, or nil.
- *
- * @discusssion
- *     This function builds a new class based on the information passed in
- *     the arguments. The methods and functions in the class_dict are 
- *     transformed into selector objects.
- *
- *     The function will fail if the class does not in fact implement the
- *     protocols in the protocol list, or does partially implement any other
- *     known (informal) protocol.
- */
 Class PyObjCClass_BuildClass(
 		Class super_class,  
 		PyObject* protocols,
 		char* name, 
 		PyObject* class_dict,
-		PyObject* meta_dict);
+		PyObject* meta_dict,
+		PyObject* hiddenSelectors);
 
-/*!
- * @function PyObjCClass_UnbuildClass
- * @abstract Undo the work of PyObjCClass_BuildClass
- * @param Class A class created by PyObjCClass_BuildClass
- * @result 0 on success, -1 on failure
- * @discussion
- *    This function destroys the class created by PyObjCClass_BuildClass. This
- *    function can only be called when PyObjCClass_FinishClass has not been 
- *    called for the class.
- *
- *    This limitation is necessary because it is not possible to remove classes
- *    from the Objetive-C runtime on MacOS X.
- */
 int PyObjCClass_UnbuildClass(Class new_class);
-
-
-/*!
- * @function PyObjCClass_FinishClass
- * @abstract Register the class in the Objective-C runtime
- * @param objc_class A class created by PyObjCClass_BuildClass
- * @result Returns 0 on success, -1 on failure.
- * @discussion
- *    This function updates the bookkeeping information for objc_class and
- *    then registers the class with the Objective-C runtime.
- */
 int PyObjCClass_FinishClass(Class objc_class);
-
-
 void PyObjC_RemoveInternalTypeCodes(char* buf);
 
 #endif /* OBJC_CLASS_BUILDER */

Modified: trunk/pyobjc/pyobjc-core/Modules/objc/class-builder.m
==============================================================================
--- trunk/pyobjc/pyobjc-core/Modules/objc/class-builder.m	(original)
+++ trunk/pyobjc/pyobjc-core/Modules/objc/class-builder.m	Thu Jan 28 04:44:28 2010
@@ -6,6 +6,8 @@
 
 #import <Foundation/NSInvocation.h>
 
+PyObject* PyObjC_class_setup_hook = NULL;
+
 #if !defined(MAC_OS_X_VERSION_MIN_REQUIRED) || MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_4
 
 /* 
@@ -608,8 +610,10 @@
 
 Class 
 PyObjCClass_BuildClass(Class super_class,  PyObject* protocols,
-		char* name, PyObject* class_dict, PyObject* meta_dict)
+		char* name, PyObject* class_dict, PyObject* meta_dict,
+		PyObject* hiddenSelectors)
 {
+	PyObject* seq;
 	PyObject*                key_list = NULL;
 	PyObject*                key = NULL;
 	PyObject*                value = NULL;
@@ -621,9 +625,11 @@
 	Class		 	 new_meta_class = NULL;
 	Class                    cur_class;
 	PyObject*                py_superclass = NULL;
-	Py_ssize_t               item_size;
 	int			 have_intermediate = 0;
 	int			 need_intermediate = 0;
+	PyObject* 		 instance_variables = NULL;
+	PyObject* 		 instance_methods = NULL;
+	PyObject* 		 class_methods = NULL;
 
 	if (!PyList_Check(protocols)) {
 		PyErr_Format(PyObjCExc_InternalError,  
@@ -664,7 +670,26 @@
 	PyDict_SetItemString(class_dict, "__objc_python_subclass__", Py_True);
 
 	py_superclass = PyObjCClass_New(super_class);
-	if (py_superclass == NULL) return NULL;
+	if (py_superclass == NULL) {
+		return NULL;
+	}
+
+	instance_variables = PySet_New(NULL);
+	if (instance_variables == NULL) {
+		return NULL;
+	}
+
+	instance_methods = PySet_New(NULL);
+	if (instance_methods == NULL) {
+		Py_DECREF(instance_variables);
+		return NULL;
+	}
+	class_methods = PySet_New(NULL);
+	if (class_methods == NULL) {
+		Py_DECREF(instance_variables);
+		Py_DECREF(instance_methods);
+		return NULL;
+	}
 
 	/* We must override copyWithZone: for python classes because the
 	 * refcounts of python slots might be off otherwise. Yet it should
@@ -847,19 +872,77 @@
 		goto error_cleanup;
 	}
 
-	/* First round, count new instance-vars and check for overridden 
-	 * methods.
-	 */
+	/* First step: call class setup hooks of entries in the class dict */
 	for (i = 0; i < key_count; i++) {
 		key = PyList_GET_ITEM(key_list, i);
-#if 0
-		if (PyErr_Occurred()) {
+
+		value = PyDict_GetItem(class_dict, key);
+		if (value == NULL) {
 			PyErr_SetString(PyObjCExc_InternalError,
 				"PyObjCClass_BuildClass: "
-				"Cannot fetch key in keylist");
+				"Cannot fetch item in keylist");
 			goto error_cleanup;
 		}
-#endif
+
+		/*
+		 * Check if the value has a class-setup hook, and if it does
+		 * call said hook.
+		 */
+		PyObject* m = PyObject_GetAttrString(value, 
+				"__pyobjc_class_setup__");
+		if (m == NULL) {
+			PyErr_Clear();
+
+		} else {
+			PyObject* rv = PyObject_CallFunction(m, "OOOO", 
+					key, class_dict, 
+					instance_methods,
+					class_methods);
+			Py_DECREF(m);
+			if (rv == NULL) {
+				goto error_cleanup;
+			}
+			Py_DECREF(rv);
+		}
+	}
+
+	Py_DECREF(key_list);
+
+	/* Second step: call global class construction hook */
+	if (PyObjC_class_setup_hook != NULL) {
+		PyObject* rv = PyObject_CallFunction(
+			PyObjC_class_setup_hook,
+			"sOOOOO", name, py_superclass, 
+			class_dict, instance_variables, 
+			instance_methods, class_methods);
+		if (rv == NULL) {
+			goto error_cleanup;
+		}
+
+		/* Todo: do we need to do something with a result? */
+		Py_XDECREF(rv);
+	}
+
+
+	/* The class hooks can modify the class dict, recalculate the key list */
+	key_list = PyDict_Keys(class_dict);
+	if (key_list == NULL) {
+		goto error_cleanup;
+	}
+
+	key_count = PyList_Size(key_list);
+	if (PyErr_Occurred()) {
+		Py_DECREF(key_list);
+		goto error_cleanup;
+	}
+
+	/* Step 2b: Collect methods and instance variables in the class dict
+	 *          into the 3 sets.
+	 *
+	 * FIXME: This work should be done by the class setup hook instead.
+	 */
+	for (i = 0; i < key_count; i++) {
+		key = PyList_GET_ITEM(key_list, i);
 
 		value = PyDict_GetItem(class_dict, key);
 		if (value == NULL) {
@@ -870,64 +953,89 @@
 		}
 
 		if (PyObjCInstanceVariable_Check(value)) {
-			if (PyObjCInstanceVariable_SetName(value, key) == -1) {
+			if (PySet_Add(instance_variables, value) == -1) {
 				goto error_cleanup;
 			}
-			if (class_getInstanceVariable(super_class, 
-			    ((PyObjCInstanceVariable*)value)->name) != NULL) {
-				PyErr_Format(PyObjCExc_Error,
-					"a superclass already has an instance "
-					"variable with this name: %s",
-					((PyObjCInstanceVariable*)value)->name);
+
+		} else if (PyObjCSelector_Check(value)) {
+			int r;
+
+			/* Check if the 'key' is the name as the python 
+			 * representation of our selector. If not: add the
+			 * python representation of our selector to the 
+			 * dict as well to ensure that the ObjC interface works
+			 * from Python as well.
+			 *
+			 * NOTE: This also allows one to add both a class
+			 * and instance method for the same selector in one
+			 * generation.
+			 */
+			char buf[1024];
+			PyObject* pyname = PyText_FromString(
+				PyObjC_SELToPythonName(
+					PyObjCSelector_GetSelector(value), 
+					buf, sizeof(buf)));
+			if (pyname == NULL) {
 				goto error_cleanup;
 			}
-
-			if (((PyObjCInstanceVariable*)value)->isSlot) {
-				item_size = sizeof(PyObject**);
-			} else {
-				item_size = PyObjCRT_SizeOfType(
-					((PyObjCInstanceVariable*)value)->type);
+			int shouldCopy = PyObject_RichCompareBool(pyname, key, Py_EQ);
+			if (shouldCopy == -1) {
+				goto error_cleanup;
+			} else if (!shouldCopy) {
+				Py_DECREF(pyname); pyname = NULL;
 			}
-			if (item_size == -1) goto error_cleanup;
 
-
-		} else if (PyObjCNativeSelector_Check(value)) {
-			char methType = '-';
-			if (PyObjCSelector_IsClassMethod(value)) {
-				methType = '+';
+			if (PyObjCSelector_GetClass(value) != NULL) {
+				PyObject* new_value;
+				new_value = PyObjCSelector_Copy(value);
+				if (new_value == NULL) {
+					goto error_cleanup;
+				}
+				if (PyDict_SetItem(class_dict, key, new_value) == -1) {
+					Py_DECREF(new_value);
+					goto error_cleanup;
+				}
+				value = new_value;
+				Py_DECREF(new_value); /* The value is still in the dict, and hence safe to use */
 			}
-			PyErr_Format(PyExc_TypeError,
-				"native selector %c%s of %s",
-				methType, 
-				sel_getName(PyObjCSelector_GetSelector(value)),
-				class_getName(PyObjCSelector_GetClass(value)));
-			goto error_cleanup;
 
-		} else if (PyObjCSelector_Check(value)) {
-			PyObjCSelector* sel = (PyObjCSelector*)value;
+			if (PyObjCSelector_IsClassMethod(value)) {
+				r = PySet_Add(class_methods, value);
+				if (r == -1) {
+					goto error_cleanup;
+				}
 
-			/* If it already has a sel_class, create a copy */
-#if 0
-			if (sel->sel_class != NULL) {
-				value = PyObjCSelector_Copy(value);
-				if (value == NULL) goto error_cleanup;
-				if (PyDict_SetItem(class_dict, key, value) == -1) {
-					Py_DECREF(value);
+				if (shouldCopy) {
+					r = PyDict_SetItem(meta_dict, pyname, value);
+					Py_DECREF(pyname);
+					if (r == -1)  {
+						goto error_cleanup;
+					}
+				}
+				if (PyDict_SetItem(meta_dict, key, value) == -1) {
+					goto error_cleanup;
+				}
+				if (PyDict_DelItem(class_dict, key) == -1) {
 					goto error_cleanup;
 				}
-				Py_DECREF(value);
-				sel = (PyObjCSelector*)value;
+			} else {
+				r = PySet_Add(instance_methods, value);
+				if (shouldCopy) {
+					r = PyDict_SetItem(class_dict, pyname, value);
+					Py_DECREF(pyname);
+					if (r == -1) {
+						goto error_cleanup;
+					}
+				}
 			}
-#endif
 
-			/* Set sel_class */
-			sel->sel_class = new_class;
 
 		} else if (
 				PyMethod_Check(value) 
 			     || PyFunction_Check(value) 
 			     || PyObject_TypeCheck(value, &PyClassMethod_Type)){
 
+
 			PyObject* pyname;
 			char*     ocname;
 			pyname = key;
@@ -955,39 +1063,169 @@
 				goto error_cleanup;
 			}
 			
-			if (ocname[0] == '_' && ocname[1] == '_') {
+			if (ocname[0] != '_' || ocname[1] != '_') {
 				/* Skip special methods (like __getattr__) to
 				 * avoid confusing type().
 				 */
-				Py_XDECREF(pyname_bytes);
-				continue;
-			}
+				PyObject* new_value;
 
-			value = PyObjCSelector_FromFunction(
+				new_value = PyObjCSelector_FromFunction(
 					pyname,
 					value,
 					py_superclass,
 					protocols);
-			if (value == NULL) {
-				Py_XDECREF(pyname_bytes);
-				goto error_cleanup;
-			}
+				if (new_value == NULL) {
+					Py_CLEAR(pyname_bytes);
+					goto error_cleanup;
+				}
+				value = new_value;
 
-			if (!PyObjCSelector_Check(value)) {
-				Py_XDECREF(pyname_bytes);
-				Py_DECREF(value);
-				continue;
+				Py_CLEAR(pyname_bytes);
+
+				if (PyObjCSelector_Check(value)) {
+					int r;
+
+
+					if (PyObjCSelector_IsClassMethod(value)) {
+						if (PyDict_SetItem(meta_dict, key, value) == -1) {
+							goto error_cleanup;
+						}
+						if (PyDict_DelItem(class_dict, key) == -1) {
+							goto error_cleanup;
+						}
+
+						r = PySet_Add(class_methods, value);
+
+					} else {
+						if (PyDict_SetItem(class_dict, key, value) < 0) {
+							Py_CLEAR(value);
+							goto error_cleanup;
+						}
+
+						r = PySet_Add(instance_methods, value);
+					}
+					if (r == -1) {
+						goto error_cleanup;
+					}
+				}
 			}
+			Py_CLEAR(pyname_bytes);
+		}
+	}
 
-			((PyObjCSelector*)value)->sel_class = new_class;
+	/* Keylist is not needed anymore */
+	Py_DECREF(key_list); key_list = NULL;
 
-			if (PyDict_SetItem(class_dict, key, value) < 0) {
-				Py_XDECREF(pyname_bytes);
-				Py_DECREF(value); value = NULL;
-				goto error_cleanup;
+	/* Step 3: Check instance variables */
+
+	/*    convert to 'fast sequence' to ensure stable order when accessing */
+	seq = PySequence_Fast(instance_variables, "converting instance variable set to sequence");
+	if (seq == NULL) {
+		goto error_cleanup;
+	}
+	Py_DECREF(instance_variables);
+	instance_variables = seq;
+	for (i = 0; i < PySequence_Fast_GET_SIZE(instance_variables); i++) {
+		value = PySequence_Fast_GET_ITEM(instance_variables, i);
+
+		if (!PyObjCInstanceVariable_Check(value)) {
+			continue;
+		}
+
+		/* Our only check for now is that instance variable names must be unique */
+		/* XXX: Is this really necessary? */
+		if (class_getInstanceVariable(super_class, PyObjCInstanceVariable_GetName(value)) != NULL) {
+			PyErr_Format(PyObjCExc_Error,
+				"a superclass already has an instance "
+				"variable with this name: %s",
+				PyObjCInstanceVariable_GetName(value));
+			goto error_cleanup;
+		}
+	}
+
+	/* Step 4: Verify instance and class methods sets */
+
+	/*   first convert then to 'Fast' sequences for easier access */
+	seq = PySequence_Fast(instance_methods, "converting instance method set to sequence");
+	if (seq == NULL) {
+		goto error_cleanup;
+	}
+	Py_DECREF(instance_methods);
+	instance_methods = seq;
+
+	seq = PySequence_Fast(class_methods, "converting class method set to sequence");
+	if (seq == NULL) {
+		goto error_cleanup;
+	}
+	Py_DECREF(class_methods);
+	class_methods = seq;
+
+	for (i = 0; i < PySequence_Fast_GET_SIZE(instance_methods); i++) {
+		value = PySequence_Fast_GET_ITEM(instance_methods, i);
+
+		if (!PyObjCSelector_Check(value)) {
+			continue;
+		}
+
+		if (PyObjCSelector_IsClassMethod(value)) {
+			PyErr_Format(PyExc_TypeError,
+				"class method in instance method set: -%s",
+				sel_getName(PyObjCSelector_GetSelector(value)));
+			goto error_cleanup;
+		}
+
+		if (PyObjCNativeSelector_Check(value)) {
+			PyErr_Format(PyExc_TypeError,
+				"native selector -%s of %s",
+				sel_getName(PyObjCSelector_GetSelector(value)),
+				class_getName(PyObjCSelector_GetClass(value)));
+			goto error_cleanup;
+		} else if (PyObjCSelector_Check(value)) {
+			PyObjCSelector* sel = (PyObjCSelector*)value;
+
+			/* Set sel_class */
+			sel->sel_class = new_class;
+
+			if (sel->sel_flags & PyObjCSelector_kHIDDEN) {
+				PyObject* v = PyBytes_InternFromString(
+					sel_getName(PyObjCSelector_GetSelector(value)));
+				if (v == NULL) {
+					goto error_cleanup;
+				}
+				int r = PySet_Add(hiddenSelectors, v);
+				Py_DECREF(v);
+				if (r == -1) {
+					goto error_cleanup;
+				}
 			}
-			Py_XDECREF(pyname_bytes);
-			Py_DECREF(value); value = NULL;
+		}
+	}
+	for (i = 0; i < PySequence_Fast_GET_SIZE(class_methods); i++) {
+		value = PySequence_Fast_GET_ITEM(class_methods, i);
+
+		if (!PyObjCSelector_Check(value)) {
+			continue;
+		}
+
+		if (!PyObjCSelector_IsClassMethod(value)) {
+			PyErr_Format(PyExc_TypeError,
+				"instance method in class method set: -%s",
+				sel_getName(PyObjCSelector_GetSelector(value)));
+			goto error_cleanup;
+		}
+
+
+		if (PyObjCNativeSelector_Check(value)) {
+			PyErr_Format(PyExc_TypeError,
+				"native selector +%s of %s",
+				sel_getName(PyObjCSelector_GetSelector(value)),
+				class_getName(PyObjCSelector_GetClass(value)));
+			goto error_cleanup;
+		} else if (PyObjCSelector_Check(value)) {
+			PyObjCSelector* sel = (PyObjCSelector*)value;
+
+			/* Set sel_class */
+			sel->sel_class = new_class;
 		}
 	}
 
@@ -1115,161 +1353,136 @@
 #undef		METH
 	}
 
-	for (i = 0; i < key_count; i++) {
-		key = PyList_GetItem(key_list, i);
-		if (key == NULL) {
-			PyErr_SetString(PyObjCExc_InternalError,
-				"PyObjCClass_BuildClass: "
-				"Cannot fetch key in keylist");
-			goto error_cleanup;
+	/* add instance variables */
+	for (i = 0; i < PySequence_Fast_GET_SIZE(instance_variables); i++) {
+		value = PySequence_Fast_GET_ITEM(instance_variables, i);
+
+		if (!PyObjCInstanceVariable_Check(value)) {
+			continue;
 		}
 
-		value = PyDict_GetItem(class_dict, key);
-		if (value == NULL)  {
-			PyErr_SetString(PyObjCExc_InternalError,
-				"PyObjCClass_BuildClass: "
-				"Cannot fetch item in keylist");
+		char* type;
+		size_t size;
+		size_t align;
+
+
+		if (PyObjCInstanceVariable_IsSlot(value)) {
+			type = @encode(PyObject*);
+			size = sizeof(PyObject*);
+		} else {
+			type = PyObjCInstanceVariable_GetType(value);
+			size = PyObjCRT_SizeOfType(type);
+		}
+		align = PyObjCRT_AlignOfType(type);
+
+
+		if (PyObjCInstanceVariable_GetName(value) == NULL) {
+			PyErr_SetString(PyObjCExc_Error,
+				"instance variable without a name");
 			goto error_cleanup;
 		}
 
-		if (PyObjCInstanceVariable_Check(value)) {
-			char* type;
-			size_t size;
-			size_t align;
+		if (!preclass_addIvar(new_class, 
+			PyObjCInstanceVariable_GetName(value),
+			size,
+			align,
+			type
+			)) {
 
+			goto error_cleanup;
+		}
+	}
 
-			if (PyObjCInstanceVariable_IsSlot(value)) {
-				type = @encode(PyObject*);
-				size = sizeof(PyObject*);
-			} else {
-				type = PyObjCInstanceVariable_GetType(value);
-				size = PyObjCRT_SizeOfType(type);
-			}
-			align = PyObjCRT_AlignOfType(type);
+	/* instance methods */
+	for (i = 0; i < PySequence_Fast_GET_SIZE(instance_methods); i++) {
+		value = PySequence_Fast_GET_ITEM(instance_methods, i);
 
-			if (!preclass_addIvar(new_class, 
-				PyObjCInstanceVariable_GetName(value),
-				size,
-				align,
-				type
-				)) {
+		if (!PyObjCSelector_Check(value)) {
+			continue;
+		}
 
+		Method	      meth;
+		int           is_override = 0;
+		IMP	      imp;
+
+		meth = class_getInstanceMethod(super_class,
+			PyObjCSelector_GetSelector(value));
+		if (meth) {
+			is_override = 1;
+			if (!same_signature(method_getTypeEncoding(meth), 
+				PyObjCSelector_GetNativeSignature(value))) {
+				
+				PyErr_Format(PyObjCExc_BadPrototypeError,
+					"%R has signature that is not compatible with super-class",
+					value);
 				goto error_cleanup;
 			}
+		}
+		if (is_override) {
+			imp = PyObjC_MakeIMP(new_class, super_class, value, value);
+		} else  {
+			imp = PyObjC_MakeIMP(new_class, nil, value, value);
+		}
+		if (imp == NULL) {
+			goto error_cleanup;
+		}
 
-		} else if (PyObjCSelector_Check(value)) {
-			PyObjCSelector* sel = (PyObjCSelector*)value;
-			Method	      meth;
-			int           is_override = 0;
-			Class	      cls;
-			IMP	      imp;
-			
-			/* Check if the 'key' is the name as the python 
-			 * representation of our selector. If not: add the
-			 * python representation of our selector to the 
-			 * dict as well to ensure that the ObjC interface works
-			 * from Python as well.
-			 *
-			 * NOTE: This also allows one to add both a class
-			 * and instance method for the same selector in one
-			 * generation.
-			 */
-			char buf[1024];
-			PyObject* pyname = PyText_FromString(
-				PyObjC_SELToPythonName(sel->sel_selector, buf, sizeof(buf)));
-			if (pyname == NULL) goto error_cleanup;
-			int shouldCopy = PyObject_RichCompareBool(pyname, key, Py_EQ);
-			if (shouldCopy == -1) goto error_cleanup;
-
-
-			if (sel->sel_flags & PyObjCSelector_kCLASS_METHOD) {
-				meth = class_getClassMethod(super_class,
-					sel->sel_selector);
-				if (meth) {
-					is_override = 1;
-
-					if (!same_signature(method_getTypeEncoding(meth), 
-						sel->sel_native_signature)) {
-					
-						PyErr_Format(PyObjCExc_BadPrototypeError,
-							"%R has signature that is not compatible with super-class",
-							sel);
-						goto error_cleanup;
-					}
-				}
-				cls = new_meta_class;
-
-				/* Class method: the value should be in the
-				 * metadict instead of the regular dict. 
-				 * Make it so.
-				 */
-				if (shouldCopy) {
-					if (PyDict_SetItem(meta_dict, pyname, value) == -1) {
-						Py_DECREF(pyname);
-						goto error_cleanup;
-					}
-				}
-				Py_DECREF(pyname);
+		if (!preclass_addMethod(new_class, PyObjCSelector_GetSelector(value), imp,
+					PyObjCSelector_GetNativeSignature(value))) {
+			goto error_cleanup;
+		}
+	}
 
-				if (PyDict_SetItem(meta_dict, key, value) == -1)
-					goto error_cleanup;
-				if (PyDict_DelItem(class_dict, key) == -1)
-					goto error_cleanup;
+	/* class methods */
+	for (i = 0; i < PySequence_Fast_GET_SIZE(class_methods); i++) {
+		value = PySequence_Fast_GET_ITEM(class_methods, i);
 
+		if (!PyObjCSelector_Check(value)) {
+			continue;
+		}
 
+		Method	      meth;
+		int           is_override = 0;
+		IMP	      imp;
 
-			} else {
-				meth = class_getInstanceMethod(super_class,
-					sel->sel_selector);
-				if (meth) {
-					is_override = 1;
-					if (!same_signature(method_getTypeEncoding(meth), 
-						sel->sel_native_signature)) {
-						
-						PyErr_Format(PyObjCExc_BadPrototypeError,
-							"%R has signature that is not compatible with super-class",
-							sel);
-						goto error_cleanup;
-					}
-				}
-				cls = new_class;
 
-				if (shouldCopy) {
-					if (PyDict_SetItem(class_dict, pyname, value) == -1) {
-						Py_DECREF(pyname);
-						goto error_cleanup;
-					}
-				}
-				Py_DECREF(pyname);
-			}
-		
-			if (is_override) {
-				imp = PyObjC_MakeIMP(cls, super_class, value, value);
-			} else  {
-				imp = PyObjC_MakeIMP(cls, nil, value, value);
-			}
-			if (imp == NULL) {
-				goto error_cleanup;
+		meth = class_getClassMethod(super_class, PyObjCSelector_GetSelector(value));
+		if (meth) {
+			is_override = 1;
+
+			if (!same_signature(method_getTypeEncoding(meth), 
+					PyObjCSelector_GetNativeSignature(value))) {
+				
+				PyErr_Format(PyObjCExc_BadPrototypeError,
+					"%R has signature that is not compatible with super-class",
+					value);
+					goto error_cleanup;
 			}
+		}
 
-			if (!preclass_addMethod(cls, sel->sel_selector, imp,
-						sel->sel_native_signature)) {
-				goto error_cleanup;
-			}
+		if (is_override) {
+			imp = PyObjC_MakeIMP(new_meta_class, super_class, value, value);
+		} else  {
+			imp = PyObjC_MakeIMP(new_meta_class, nil, value, value);
+		}
+		if (imp == NULL) {
+			goto error_cleanup;
+		}
 
-			if (sel->sel_class == NULL) {
-				sel->sel_class = new_class;
-			}
-		} /* XXX: else if (PyObjCIMP_Check(value)) */
+		if (!preclass_addMethod(new_meta_class, PyObjCSelector_GetSelector(value), imp,
+					PyObjCSelector_GetNativeSignature(value))) {
+			goto error_cleanup;
+		}
 	}
-	Py_DECREF(key_list);
-	key_list = NULL;
 
 	Py_XDECREF(py_superclass); py_superclass = NULL;
 
 	if (PyDict_DelItemString(class_dict, "__dict__") < 0) {
 		PyErr_Clear();
 	}
+	Py_XDECREF(instance_variables); instance_variables = NULL;
+	Py_XDECREF(instance_methods); instance_methods = NULL;
+	Py_XDECREF(class_methods); class_methods = NULL;
 
 	/* 
 	 * NOTE: Class is not registered yet, we do that as lately as possible
@@ -1279,6 +1492,9 @@
 	return new_class;
 
 error_cleanup:
+	Py_XDECREF(instance_variables);
+	Py_XDECREF(instance_methods);
+	Py_XDECREF(class_methods);
 	Py_XDECREF(py_superclass);
 
 	if (key_list) {

Modified: trunk/pyobjc/pyobjc-core/Modules/objc/instance-var.m
==============================================================================
--- trunk/pyobjc/pyobjc-core/Modules/objc/instance-var.m	(original)
+++ trunk/pyobjc/pyobjc-core/Modules/objc/instance-var.m	Thu Jan 28 04:44:28 2010
@@ -266,6 +266,48 @@
 	return 0;
 }
 
+static PyObject*
+ivar_class_setup(PyObject* _self, PyObject* args, PyObject* kwds)
+{
+static 	char* keywords[] = { "name", "class_dict", "instance_method_list", "class_method_list", NULL };
+	PyObjCInstanceVariable* self = (PyObjCInstanceVariable*)_self;
+	char* name;
+	PyObject* class_dict;
+	PyObject* instance_method_list;
+	PyObject* class_method_list;
+
+	if (!PyArg_ParseTupleAndKeywords(args, kwds, "sO!O!O!", keywords,
+			&name,
+			&PyDict_Type, &class_dict, 
+			&PySet_Type, &instance_method_list,
+			&PySet_Type, &class_method_list
+		)){
+		return NULL;
+	}
+
+	if (self->name == NULL) {
+		self->name = PyObjCUtil_Strdup(name);
+	}
+
+	Py_INCREF(Py_None);
+	return Py_None;
+}
+
+
+static PyMethodDef ivar_methods[] = {
+	{
+		"__pyobjc_class_setup__",
+		(PyCFunction)ivar_class_setup,
+		METH_VARARGS|METH_KEYWORDS,
+		NULL
+	},
+
+	{
+		NULL, NULL, 0, NULL
+	}
+};
+
+
 PyDoc_STRVAR(ivar_doc,
 "ivar(name, type='@', isOutlet=False) -> instance-variable\n"
 "\n"
@@ -307,7 +349,7 @@
 	0,                                      /* tp_weaklistoffset */
 	0,                                      /* tp_iter */
 	0,                                      /* tp_iternext */
-	0,                                      /* tp_selectors */
+	ivar_methods,                           /* tp_methods */
 	0,                                      /* tp_members */
 	0,                                      /* tp_getset */
 	0,                                      /* tp_base */

Modified: trunk/pyobjc/pyobjc-core/Modules/objc/libffi_support.m
==============================================================================
--- trunk/pyobjc/pyobjc-core/Modules/objc/libffi_support.m	(original)
+++ trunk/pyobjc/pyobjc-core/Modules/objc/libffi_support.m	Thu Jan 28 04:44:28 2010
@@ -156,6 +156,7 @@
 
 #if PY_VERSION_HEX < 0x03000000
 static void cleanup_ffitype_capsule(void* ptr, void* context __attribute__((__unused__)))
+
 {
 	free_type(ptr);
 }

Modified: trunk/pyobjc/pyobjc-core/Modules/objc/module.m
==============================================================================
--- trunk/pyobjc/pyobjc-core/Modules/objc/module.m	(original)
+++ trunk/pyobjc/pyobjc-core/Modules/objc/module.m	Thu Jan 28 04:44:28 2010
@@ -25,6 +25,7 @@
 int PyObjC_VerboseLevel = 0;
 int PyObjC_HideProtected = 1;
 BOOL PyObjC_useKVO = YES;
+BOOL PyObjC_nativeProperties = NO;
 
 PyObject* PyObjCClass_DefaultModule = NULL;
 PyObject* PyObjC_NSNumberWrapper = NULL;
@@ -1527,7 +1528,7 @@
   "C. This object has no useable interface from Python.\n"
  );
 #if PY_VERSION_HEX < 0x03000000
-static void _callback_cleanup(void* closure, void* unused __attribute__((__unused__)))
+static void _callback_cleanup(void* closure)
 {
 	PyObjCFFI_FreeIMP((IMP)closure);
 }
@@ -1645,16 +1646,46 @@
 	return object;
 }
 
+static PyObject*
+mod_propertiesForClass(PyObject* mod __attribute__((__unused__)), PyObject* object)
+{
+	return PyObjCClass_ListProperties(object);
+}
+
+static PyObject* 
+mod_setClassSetupHook(PyObject* mod __attribute__((__unused__)), PyObject* hook)
+{
+	PyObject* curval = PyObjC_class_setup_hook;
+
+	PyObjC_class_setup_hook = hook;
+	Py_INCREF(hook);
+
+	return curval;
+}
+
 
 static PyMethodDef mod_methods[] = {
 	{
+		"_setClassSetUpHook",
+		(PyCFunction)mod_setClassSetupHook,
+		METH_O,
+		"Private: set hook used during subclass creation",
+	},
+
+	{
+		"propertiesForClass",
+	  	(PyCFunction)mod_propertiesForClass,
+		METH_O,
+		"Return information about properties from the runtim",
+	},
+	{
 	  "splitSignature",
 	  (PyCFunction)objc_splitSignature,
 	  METH_VARARGS|METH_KEYWORDS,
 	  objc_splitSignature_doc
 	},
 	{
-	  "_splitStruct",
+	  "splitStruct",
 	  (PyCFunction)objc_splitStruct,
 	  METH_VARARGS|METH_KEYWORDS,
 	  objc_splitStruct_doc,
@@ -1673,7 +1704,7 @@
 	},
 	{ "currentBundle", (PyCFunction)currentBundle, METH_NOARGS, currentBundle_doc },
 	{ "getClassList", (PyCFunction)getClassList, METH_NOARGS, getClassList_doc },
-	{ "setClassExtender", (PyCFunction)set_class_extender, METH_VARARGS|METH_KEYWORDS, set_class_extender_doc  },
+	{ "_setClassExtender", (PyCFunction)set_class_extender, METH_VARARGS|METH_KEYWORDS, set_class_extender_doc  },
 	{ "setSignatureForSelector", (PyCFunction)set_signature_for_selector, METH_VARARGS|METH_KEYWORDS, set_signature_for_selector_doc },
 	{ "recycleAutoreleasePool", (PyCFunction)recycle_autorelease_pool, METH_VARARGS|METH_KEYWORDS, recycle_autorelease_pool_doc },
 	{ "removeAutoreleasePool", (PyCFunction)remove_autorelease_pool, METH_VARARGS|METH_KEYWORDS, remove_autorelease_pool_doc },

Modified: trunk/pyobjc/pyobjc-core/Modules/objc/objc-class.h
==============================================================================
--- trunk/pyobjc/pyobjc-core/Modules/objc/objc-class.h	(original)
+++ trunk/pyobjc/pyobjc-core/Modules/objc/objc-class.h	Thu Jan 28 04:44:28 2010
@@ -94,6 +94,7 @@
 	int generation;
 	int useKVO;
 	PyObject* protectedMethods;
+	PyObject* hiddenSelectors;
 	struct _PyObjCClassObject* meta_class; /* To be dropped */
 } PyObjCClassObject;
 
@@ -109,5 +110,9 @@
 void PyObjCClass_SetDelMethod(PyObject* cls, PyObject* newval);
 int  PyObjCClass_HasPythonImplementation(PyObject* cls);
 PyObject* PyObjCClass_ClassForMetaClass(PyObject* meta);
+BOOL PyObjCClass_HiddenSelector(PyObject* tp, SEL sel);
+
+PyObject* PyObjCClass_ListProperties(PyObject* cls);
+
 
 #endif /* PyObjC_OBJC_CLASS_H */

Modified: trunk/pyobjc/pyobjc-core/Modules/objc/objc-class.m
==============================================================================
--- trunk/pyobjc/pyobjc-core/Modules/objc/objc-class.m	(original)
+++ trunk/pyobjc/pyobjc-core/Modules/objc/objc-class.m	Thu Jan 28 04:44:28 2010
@@ -7,6 +7,41 @@
 
 #include <stddef.h>
 
+
+BOOL PyObjCClass_HiddenSelector(PyObject* tp, SEL sel)
+{
+	PyObject* mro = ((PyTypeObject*)tp)->tp_mro;
+	int i, n;
+
+	if (mro == NULL) {
+		return NO;
+	}
+	assert(PyTuple_Check(mro));
+	n = PyTuple_GET_SIZE(mro);
+	for (i = 0; i < n; i++) {
+		PyObject* base = PyTuple_GET_ITEM(mro, i);
+		if (PyObjCClass_Check(base)) {
+			PyObject* hidden = ((PyObjCClassObject*)base)->hiddenSelectors;
+			if (hidden != NULL) {
+				PyObject* v = PyBytes_InternFromString(sel_getName(sel));
+				if (v == NULL) {
+					PyErr_Clear();
+				} else {
+					int r = PySet_Contains(hidden, v);
+					Py_DECREF(v);
+					if (r == -1) {
+						PyErr_Clear();
+					} else if (r == 1) {
+						return YES;
+					}
+				}
+			}
+		}
+	}
+
+	return NO;
+}
+
 /*
  * Support for NSData/NSMutableData to have buffer API
  *
@@ -119,7 +154,7 @@
 
 PyObject* PyObjC_ClassExtender = NULL;
 
-static int add_class_fields(Class objc_class, PyObject* dict, PyObject* protDict, PyObject* classDict); 
+static int add_class_fields(Class objc_class, PyObject* py_class, PyObject* dict, PyObject* protDict, PyObject* classDict); 
 static int add_convenience_methods(Class cls, PyObject* type_dict);
 static int update_convenience_methods(PyObject* cls);
 
@@ -342,6 +377,7 @@
 	PyObject* useKVOObj;
 	Ivar var;
 	PyObject* protectedMethods = NULL;
+	PyObject* hiddenSelectors = NULL;
 	BOOL      isCFProxyClass = NO;
 
 	if (!PyArg_ParseTupleAndKeywords(args, kwds, "sOO:__new__",
@@ -390,6 +426,13 @@
 		return NULL;
 	}
 
+	hiddenSelectors = PySet_New(NULL);
+	if (hiddenSelectors == NULL) {
+		Py_DECREF(protectedMethods);
+		return NULL;
+	}
+
+
 	/*
 	 * __pyobjc_protocols__ contains the list of protocols supported
 	 * by an existing class.
@@ -399,7 +442,11 @@
 	if (protocols == NULL) {
 		PyErr_Clear();
 		protocols = PyList_New(0);
-		if (protocols == NULL) return NULL;
+		if (protocols == NULL) {
+			Py_DECREF(protectedMethods);
+			Py_DECREF(hiddenSelectors);
+			return NULL;
+		}
 	} else {
 		PyObject* seq;
 		Py_ssize_t protocols_len;
@@ -407,6 +454,8 @@
 		seq = PySequence_Fast(protocols, 
 			"__pyobjc_protocols__ not a sequence?");
 		if (seq == NULL) {
+			Py_DECREF(protectedMethods);
+			Py_DECREF(hiddenSelectors);
 			Py_DECREF(protocols);
 			return NULL;
 		}
@@ -415,6 +464,8 @@
 		protocols_len = PySequence_Fast_GET_SIZE(seq);
 		protocols = PyList_New(protocols_len);
 		if (protocols == NULL) {
+			Py_DECREF(protectedMethods);
+			Py_DECREF(hiddenSelectors);
 			return NULL;
 		}
 		for (i = 0; i < protocols_len; i++) {
@@ -428,10 +479,14 @@
 	real_bases = PyList_New(0);
 	if (real_bases == NULL) {
 		Py_DECREF(protocols);
+		Py_DECREF(protectedMethods);
+		Py_DECREF(hiddenSelectors);
 		return NULL;
 	}
 	PyList_Append(real_bases, py_super_class);
 	if (PyErr_Occurred()) {
+		Py_DECREF(protectedMethods);
+		Py_DECREF(hiddenSelectors);
 		Py_DECREF(protocols);
 		Py_DECREF(real_bases);
 		return NULL;
@@ -440,11 +495,17 @@
 	for (i = 1; i < len; i++) {
 		v = PyTuple_GET_ITEM(bases, i);
 		if (v == NULL) {
+			Py_DECREF(protocols);
+			Py_DECREF(real_bases);
+			Py_DECREF(protectedMethods);
+			Py_DECREF(hiddenSelectors);
 			return NULL;
 		}
 		if (PyObjCClass_Check(v)) {
 			Py_DECREF(protocols);
 			Py_DECREF(real_bases);
+			Py_DECREF(protectedMethods);
+			Py_DECREF(hiddenSelectors);
 			PyErr_SetString(PyExc_TypeError, 
 					"multiple objective-C bases");
 			return NULL;
@@ -461,6 +522,8 @@
 	if (metadict == NULL) {
 		Py_DECREF(protocols);
 		Py_DECREF(real_bases);
+		Py_DECREF(protectedMethods);
+		Py_DECREF(hiddenSelectors);
 		return NULL;
 	}
 
@@ -471,11 +534,13 @@
 		/* First generate the objective-C class. This may change the
 		 * class dict.
 		 */
-		objc_class = PyObjCClass_BuildClass(super_class, protocols, name, dict, metadict);
+		objc_class = PyObjCClass_BuildClass(super_class, protocols, name, dict, metadict, hiddenSelectors);
 		if (objc_class == NULL) {
 			Py_DECREF(protocols);
 			Py_DECREF(metadict);
 			Py_DECREF(real_bases);
+			Py_DECREF(protectedMethods);
+			Py_DECREF(hiddenSelectors);
 			return NULL;
 		}
 
@@ -487,6 +552,8 @@
 			Py_DECREF(protocols);
 			Py_DECREF(real_bases);
 			Py_DECREF(metadict);
+			Py_DECREF(protectedMethods);
+			Py_DECREF(hiddenSelectors);
 			return NULL;
 		} else {
 			PyObjCClass_CheckMethodList(py_super_class, 1);
@@ -504,6 +571,8 @@
 		Py_DECREF(metadict);
 		Py_DECREF(protocols);
 		Py_DECREF(real_bases);
+		Py_DECREF(protectedMethods);
+		Py_DECREF(hiddenSelectors);
 		return NULL;
 	}
 	Py_DECREF(real_bases);
@@ -527,6 +596,8 @@
 				Py_DECREF(real_bases);
 				Py_DECREF(protocols);
 				Py_DECREF(metadict);
+				Py_DECREF(protectedMethods);
+				Py_DECREF(hiddenSelectors);
 				(void)PyObjCClass_UnbuildClass(objc_class);
 				return NULL;
 			}
@@ -536,6 +607,8 @@
 				Py_DECREF(real_bases);
 				Py_DECREF(protocols);
 				Py_DECREF(metadict);
+				Py_DECREF(protectedMethods);
+				Py_DECREF(hiddenSelectors);
 				(void)PyObjCClass_UnbuildClass(objc_class);
 				return NULL;
 			}
@@ -550,6 +623,8 @@
 		Py_DECREF(real_bases);
 		Py_DECREF(protocols);
 		Py_DECREF(metadict);
+		Py_DECREF(protectedMethods);
+		Py_DECREF(hiddenSelectors);
 		(void)PyObjCClass_UnbuildClass(objc_class);
 		return NULL;
 	}
@@ -576,6 +651,8 @@
 			Py_DECREF(protocols);
 			Py_DECREF(real_bases);
 			Py_DECREF(metadict);
+			Py_DECREF(protectedMethods);
+			Py_DECREF(hiddenSelectors);
 			return NULL;
 		} else {
 			if (PyDict_DelItemString(dict, "__del__") < 0) {
@@ -585,6 +662,8 @@
 				Py_DECREF(protocols);
 				Py_DECREF(real_bases);
 				Py_DECREF(metadict);
+				Py_DECREF(protectedMethods);
+				Py_DECREF(hiddenSelectors);
 				return NULL;
 			}
 		}
@@ -602,6 +681,8 @@
 		Py_DECREF(protocols);
 		Py_DECREF(real_bases);
 		Py_DECREF(metadict);
+		Py_DECREF(protectedMethods);
+		Py_DECREF(hiddenSelectors);
 		return NULL;
 	}
 		
@@ -612,6 +693,8 @@
 			Py_DECREF(protocols);
 			Py_DECREF(real_bases);
 			Py_DECREF(metadict);
+			Py_DECREF(protectedMethods);
+			Py_DECREF(hiddenSelectors);
 			return NULL;
 		}
 	}
@@ -626,6 +709,8 @@
 			Py_DECREF(protocols);
 			Py_DECREF(real_bases);
 			Py_DECREF(metadict);
+			Py_DECREF(protectedMethods);
+			Py_DECREF(hiddenSelectors);
 			return NULL;
 		}
 		if (PyDict_Update(metatype->tp_dict, metadict) == -1) {
@@ -633,6 +718,8 @@
 			Py_DECREF(protocols);
 			Py_DECREF(real_bases);
 			Py_DECREF(metadict);
+			Py_DECREF(protectedMethods);
+			Py_DECREF(hiddenSelectors);
 			return NULL;
 		}
 	} else {
@@ -654,6 +741,8 @@
 		Py_DECREF(real_bases);
 		Py_DECREF(protocols);
 		Py_DECREF(old_dict);
+		Py_DECREF(protectedMethods);
+		Py_DECREF(hiddenSelectors);
 		(void)PyObjCClass_UnbuildClass(objc_class);
 		return NULL;
 	}
@@ -675,6 +764,8 @@
 			PyObjC_UnregisterPythonProxy(objc_class, res);
 			Py_DECREF(res);
 			Py_DECREF(old_dict);
+			Py_DECREF(protectedMethods);
+			Py_DECREF(hiddenSelectors);
 			(void)PyObjCClass_UnbuildClass(objc_class);
 			return NULL;
 		}
@@ -696,6 +787,7 @@
 	info->hasPythonImpl = 1;
 	info->isCFWrapper = 0;
 	info->protectedMethods = protectedMethods;
+	info->hiddenSelectors = hiddenSelectors;
 
 
 	var = class_getInstanceVariable(objc_class, "__dict__");
@@ -795,6 +887,7 @@
 
 			r = add_class_fields(
 				info->class,
+				cls,
 				((PyTypeObject*)cls)->tp_dict,
 				info->protectedMethods,
 				Py_TYPE(cls)->tp_dict);
@@ -1348,7 +1441,7 @@
  * surprising)
  */
 static int
-add_class_fields(Class objc_class, PyObject* pubDict, PyObject* protDict, PyObject* classDict)
+add_class_fields(Class objc_class, PyObject* py_class, PyObject* pubDict, PyObject* protDict, PyObject* classDict)
 {
 	Class     cls;
 	Method*   methods;
@@ -1374,6 +1467,12 @@
 				dict = protDict;
 			}
 		}
+
+		/* Check if the selector should be hidden */
+		if (PyObjCClass_HiddenSelector(py_class, method_getName(methods[i]))) {
+			continue;
+		}
+
 		name = (char*)PyObjC_SELToPythonName(
 					method_getName(methods[i]), 
 					selbuf, 
@@ -1474,6 +1573,7 @@
 	PyObjCClassObject* info;
 	Ivar var;
 	PyObject* protectedMethods;
+	PyObject* hiddenSelectors;
 	PyTypeObject* metaclass;
 	const char* className;
 
@@ -1482,10 +1582,6 @@
 		return result;
 	}
 
-	protectedMethods = PyDict_New();
-	if (protectedMethods == NULL) {
-		return NULL;
-	}
 
 	if (class_isMetaClass(objc_class)) {
 		result =  (PyObject*)PyObjCClass_NewMetaClass(objc_class);
@@ -1493,13 +1589,26 @@
 		return result;
 	}
 
+	protectedMethods = PyDict_New();
+	if (protectedMethods == NULL) {
+		return NULL;
+	}
+
+	hiddenSelectors = PySet_New(NULL);
+	if (hiddenSelectors == NULL) {
+		Py_DECREF(protectedMethods);
+		return NULL;
+	}
+
 	metaclass = PyObjCClass_NewMetaClass(objc_class);
 	if (metaclass == NULL) {
+		Py_DECREF(hiddenSelectors);
 		Py_DECREF(protectedMethods);
 		return NULL;
 	}
 
 
+
 	dict = PyDict_New();
 	PyDict_SetItemString(dict, "__slots__", PyTuple_New(0));
 
@@ -1521,7 +1630,11 @@
 
 	result = PyType_Type.tp_new(metaclass, args, NULL);
 	Py_DECREF(args); Py_DECREF(metaclass);
-	if (result == NULL) return NULL;
+	if (result == NULL) {
+		Py_DECREF(hiddenSelectors);
+		Py_DECREF(protectedMethods);
+		return NULL;
+	}
 
 	info = (PyObjCClassObject*)result;
 	info->class = objc_class;
@@ -1533,6 +1646,7 @@
 	info->hasPythonImpl = 0;
 	info->isCFWrapper = 0;
 	info->protectedMethods = protectedMethods;
+	info->hiddenSelectors = hiddenSelectors;
 
 	/*
 	 * Support the buffer protocol in the wrappers for NSData and
@@ -1564,6 +1678,218 @@
 	return result;
 }
 
+PyObject* 
+PyObjCClass_ListProperties(PyObject* aClass)
+{
+	Class cls = Nil;
+	Protocol* proto = nil;
+
+	if (PyObjCClass_Check(aClass)) {
+		cls = PyObjCClass_GetClass(aClass);
+		if (cls == Nil) {
+			return NULL;
+		}
+	} else if (PyObjCFormalProtocol_Check(aClass)) {
+		proto = PyObjCFormalProtocol_GetProtocol(aClass);
+		if (proto == nil) {
+			return NULL;
+		}
+	} else {
+		PyErr_SetString(PyExc_TypeError, 
+		      "class must be an Objective-C class or formal protocol");
+		return NULL;
+	}
+
+	objc_property_t* props;
+	unsigned int propcount, i;
+	char buf[128];
+
+	if (cls == Nil) {
+		return NULL;
+	}
+
+	PyObject* result = PyList_New(0);
+	if (result == NULL) {
+		return NULL;
+	}
+
+	if (class_copyPropertyList == NULL) {
+		/* System without the 2.0 runtime and hence without
+		 * native properties
+		 */
+		return result;
+	}
+
+	if (cls) {
+		props = class_copyPropertyList(cls, &propcount);
+	} else {
+		props = protocol_copyPropertyList(proto, &propcount);
+	}
+	if (props == NULL) {
+		return result;
+	}
+
+	for (i = 0; i < propcount; i++) {
+		PyObject* item;
+		PyObject* v;
+		const char* name = property_getName(props[i]);
+		const char* attr = property_getAttributes(props[i]);
+		const char* e;
+
+		item = Py_BuildValue(
+#if PY_VERSION_HEX < 0x03000000
+			"{ssss}",
+#else
+			"{sssy}",
+#endif
+			"name", name,
+			"raw_attr", attr);
+		if (item == NULL) {
+			goto error;
+		}
+		if (PyList_Append(result, item) == -1) {
+			Py_DECREF(item);
+			goto error;
+		}
+		Py_DECREF(item); 
+			
+		if (*attr != 'T') {
+			/* Attribute string doesn't conform to the
+			 * 2.0 protocol, don't try to process it.
+			 */
+			continue;
+		}
+
+		e = PyObjCRT_SkipTypeSpec(attr+1);
+		if (e == NULL) {
+			goto error;
+		}
+		if (e - (attr+1) > 127) {
+			v = PyBytes_InternFromStringAndSize(attr+1, e - (attr+1));
+		} else {
+			PyObjCRT_RemoveFieldNames(buf, attr+1);
+			v = PyBytes_InternFromString(buf);
+		}
+		if (v == NULL) {
+			goto error;
+		}
+
+		if (PyDict_SetItemString(item, "typestr", v) == -1) {
+			Py_DECREF(v);
+			goto error;
+		}
+		Py_DECREF(v); v = NULL;
+
+		attr = e;
+		if (*attr == '"') {
+			e = strchr(attr+1, '"');
+			v = PyText_FromStringAndSize(attr+1, e-(attr+1));
+			if (v == NULL) {
+				goto error;
+			}
+			if (PyDict_SetItemString(item, "classname", v) == -1) {
+				Py_DECREF(v);
+				goto error;
+			}
+			Py_DECREF(v); v = NULL;
+			attr = e + 1;
+		}
+			
+		if (*attr++ != ',') {
+			/* Value doesn't conform to 2.0 protocol */
+			continue;
+		}
+
+		while (attr && *attr != '\0') {
+			switch (*attr++) {
+			case 'R':
+				if (PyDict_SetItemString(item, "readonly", Py_True) < 0) {
+					goto error;
+				}
+				break;
+			case 'C':
+				if (PyDict_SetItemString(item, "copy", Py_True) < 0) {
+					goto error;
+				}
+				break;
+			case '&':
+				if (PyDict_SetItemString(item, "retain", Py_True) < 0) {
+					goto error;
+				}
+				break;
+			case 'N':
+				if (PyDict_SetItemString(item, "nonatomic", Py_True) < 0) {
+					goto error;
+				}
+				break;
+			case 'D':
+				if (PyDict_SetItemString(item, "dynamic", Py_True) < 0) {
+					goto error;
+				}
+				break;
+			case 'W':
+				if (PyDict_SetItemString(item, "weak", Py_True) < 0) {
+					goto error;
+				}
+				break;
+			case 'P':
+				if (PyDict_SetItemString(item, "collectable", Py_True) < 0) {
+					goto error;
+				}
+				break;
+			case 'G':
+				e = strchr(attr, ',');
+				if (e == NULL) {
+					v = PyBytes_FromString(attr);
+					attr = e;
+				} else {
+					v = PyBytes_FromStringAndSize(
+						attr, e - attr);
+					attr = e;
+				}
+				if (v == NULL) {
+					goto error;
+				}
+				if (PyDict_SetItemString(item, "getter", v) < 0){
+					Py_DECREF(v);
+					goto error;
+				}
+				break;
+			case 'S':
+				e = strchr(attr, ',');
+				if (e == NULL) {
+					v = PyBytes_FromString(attr);
+					attr = e;
+				} else {
+					v = PyBytes_FromStringAndSize(
+						attr, e - attr);
+					attr = e;
+				}
+				if (v == NULL) {
+					goto error;
+				}
+				if (PyDict_SetItemString(item, "setter", v) < 0){
+					Py_DECREF(v);
+					goto error;
+				}
+				break;
+			case 'V':
+				attr = NULL;
+				break;
+			}
+		}
+	}
+	free(props); props = NULL;
+
+	return result;
+error:
+	if (props) {
+		free(props);
+	}
+	Py_XDECREF(result);
+	return NULL;
+}
+
 
 Class 
 PyObjCClass_GetClass(PyObject* cls)
@@ -1572,7 +1898,7 @@
 		PyErr_Format(PyObjCExc_InternalError,
 			"PyObjCClass_GetClass called for non-class (%s)",
 			Py_TYPE(cls)->tp_name);
-		return nil;
+		return Nil;
 	}
 	
 	return ((PyObjCClassObject*)cls)->class;
@@ -1925,3 +2251,4 @@
 
 	return PyObjCClass_New(objc_metaclass_locate(meta));
 }
+

Modified: trunk/pyobjc/pyobjc-core/Modules/objc/pyobjc.h
==============================================================================
--- trunk/pyobjc/pyobjc-core/Modules/objc/pyobjc.h	(original)
+++ trunk/pyobjc/pyobjc-core/Modules/objc/pyobjc.h	Thu Jan 28 04:44:28 2010
@@ -88,6 +88,7 @@
 
 
 extern BOOL PyObjC_useKVO;
+extern BOOL PyObjC_nativeProperties;
 extern int PyObjC_VerboseLevel;
 extern int PyObjC_HideProtected;
 #if PY_VERSION_HEX < 0x03000000

Modified: trunk/pyobjc/pyobjc-core/Modules/objc/selector.h
==============================================================================
--- trunk/pyobjc/pyobjc-core/Modules/objc/selector.h	(original)
+++ trunk/pyobjc/pyobjc-core/Modules/objc/selector.h	Thu Jan 28 04:44:28 2010
@@ -7,6 +7,7 @@
  */
 
 #define PyObjCSelector_kCLASS_METHOD          0x000001
+#define PyObjCSelector_kHIDDEN                0x000002
 #define PyObjCSelector_kREQUIRED              0x000004
 #define PyObjCSelector_kRETURNS_UNINITIALIZED 0x000010
 
@@ -56,6 +57,7 @@
 
 PyObject* PyObjCSelector_Copy(PyObject* obj);
 char* PyObjCSelector_Signature(PyObject* obj);
+#define PyObjCSelector_GetNativeSignature(obj) (((PyObjCSelector*)obj)->sel_native_signature)
 SEL   PyObjCSelector_GetSelector(PyObject* obj);
 int   PyObjCSelector_GetFlags(PyObject* obj);
 Class PyObjCSelector_GetClass(PyObject* obj);

Modified: trunk/pyobjc/pyobjc-core/Modules/objc/selector.m
==============================================================================
--- trunk/pyobjc/pyobjc-core/Modules/objc/selector.m	(original)
+++ trunk/pyobjc/pyobjc-core/Modules/objc/selector.m	Thu Jan 28 04:44:28 2010
@@ -148,6 +148,16 @@
 		return NULL;
 	}
 
+	if (((PyObjCSelector*)self)->sel_flags & PyObjCSelector_kHIDDEN) {
+		r = PyDict_SetItemString(result, "hidden", Py_True);
+	} else {
+		r = PyDict_SetItemString(result, "hidden", Py_False);
+	}
+	if (r == -1) {
+		Py_DECREF(result);
+		return NULL;
+	}
+
 	if (((PyObjCSelector*)self)->sel_flags & PyObjCSelector_kRETURNS_UNINITIALIZED) {
 		r = PyDict_SetItemString(result, "return_uninitialized_object", Py_True);
 		if (r == -1) {
@@ -193,6 +203,7 @@
 	return PyBytes_FromString(self->sel_python_signature);
 }
 
+
 PyDoc_STRVAR(base_native_signature_doc, "original Objective-C signature for the method");
 static PyObject*
 base_native_signature(PyObject* _self, void* closure __attribute__((__unused__)))
@@ -227,6 +238,23 @@
 	return 0;
 }
 
+PyDoc_STRVAR(base_hidden_doc, "If True the method is not directly accessible as an object attribute");
+static PyObject*
+base_hidden(PyObject* _self, void* closure __attribute__((__unused__)))
+{
+	return PyBool_FromLong(((PyObjCSelector*)_self)->sel_flags & PyObjCSelector_kHIDDEN);
+}
+static int
+base_hidden_setter(PyObject* _self, PyObject* newVal, void* closure __attribute__((__unused__)))
+{
+	if (PyObject_IsTrue(newVal)) {
+		((PyObjCSelector*)_self)->sel_flags |= PyObjCSelector_kHIDDEN;
+	} else {
+		((PyObjCSelector*)_self)->sel_flags &= ~PyObjCSelector_kHIDDEN;
+	}
+	return 0;
+}
+
 PyDoc_STRVAR(base_selector_doc, "Objective-C name for the method");
 static PyObject*
 base_selector(PyObject* _self, void* closure __attribute__((__unused__)))
@@ -270,6 +298,13 @@
 
 static PyGetSetDef base_getset[] = {
 	{
+		"isHidden",
+		base_hidden,
+		base_hidden_setter,
+		base_hidden_doc,
+		0
+	},
+	{
 		"isRequired",
 		base_required,
 		0,
@@ -931,9 +966,12 @@
 
 	} else {
 		/* Should not happen... */
-		result->argcount = -1;
-		abort();
-
+		result->argcount = 0;
+		char* s = sel_getName(selector);
+		while ((s = strchr(s, ':')) != NULL) {
+			result->argcount++;
+			s++;
+		}
 	}
 
 	if (class_method) {
@@ -1840,6 +1878,7 @@
 		return NULL;
 	}
 
+
 	if (PyObjCPythonSelector_Check(callable)) {
 		PyObjCPythonSelector* result;
 
@@ -2022,6 +2061,10 @@
 			is_class_method,
 			oc_class);
 	}
+	if (PyObjCClass_HiddenSelector(template_class, selector)) {
+		((PyObjCSelector*)value)->sel_flags |= PyObjCSelector_kHIDDEN;
+	}
+
 	return value;
 }
 

Modified: trunk/pyobjc/pyobjc-core/NEWS.txt
==============================================================================
--- trunk/pyobjc/pyobjc-core/NEWS.txt	(original)
+++ trunk/pyobjc/pyobjc-core/NEWS.txt	Thu Jan 28 04:44:28 2010
@@ -7,12 +7,6 @@
 Version 2.3a0
 -------------
 
-- The (undocument) module ``PyObjCTools.TestSupport`` is no longer
-  present.
-
-- Converting a negative value to an unsigned integer now causes
-  a deprecation warning, this will be a hard error once I update
-  all framework wrapper metadata. 
 
 - Initial port to Python 3.x
 
@@ -31,10 +25,14 @@
 
   TODO:
 
-  * Implement new style buffer support
+  * Implement new style buffer support when depythonifying an array of
+    C structures.
 
   * Documentation updates
 
+  * Implement python3 dict, list, ... APIs for NSDictionary and friends,
+    with unittests.
+
 - The Python 3.x port does not support transparent proxies for 'FILE*'
   "objects" because the ``file`` type in Python3 is not implemented on
   top of the C library stdio.
@@ -64,13 +62,75 @@
   ``assertFoo`` methods and deprecated the ``failIfFoo`` and ``failUnlessFoo``
   methods (simularly to what's happening in the stdlib).
 
+- Added ``objc.propertiesForClass``. This function returns information about
+  properties for a class from the Objective-C runtime. The information does
+  not include information about properties in superclasses.
+
 - TODO: ``objc.kvo_property`` support
 
+  Implementation plan: 
+
+  * Add class setup hook for entries in the dict::
+
+      class kvo_property (object):
+         def __pyobjc_class_setup__(self, name, class_dict, 
+	 		instance_method_list, class_method_list):
+	     class_dict[self.__ivar_name] = objc.ivar(self.__ivar_name)
+	     instance_method_list.append(
+	     	selector(self.__setter_func, hidden=True))
+
+    (The method body is bogus, but the idea should be clear enough).
+
+    DONE, but the API is not yet stable. I'll probably add an ivar_list
+    argument as well to enable adding instance variables that aren't directly
+    visible to python code.
+
+  * Add 'hidden' flag to selectors. If a selector is hidden you will not be
+    able to access it directly, only through pyobjc_instanceMethods or
+    pyobjc_classMethods.
+
+  * Later: objc.objc_class.__pyobjc_set_builder__(callable). 
+    This callable is called when a subclass is created and does most of 
+    the setup-work in class-builder.m.
+
+    Interface of the builder::
+
+        builder(name, bases, dict, protocols, 
+		instance_method_list, class_method_list) -> bases
+
+    The protocols list and the two method lists are empty when calling, 
+    the builder must add methods and protocols to the appopriate lists.
+
+    The return value is the bases tuple for the created class, calculated
+    from the bases tuple passed in (e.g. remove protocols, possibly
+    introduce and intermediate class)
+
+    The build is responsible for converting function objects in the class
+    dict to ``objc.selector`` instances and adding them to the appropriate 
+    list, the C code will not do that.
+
+    NOTE: This API is not stable and might change in future versions of PyObjC.
+
+  * Later: automaticly parse the property definitions in the objc runtime
+    and do the right thing (but: also add global flag to enable/disable this)
+
+    Partially done: there is a function for parsing property definitions, but
+    that isn't used yet.
+
+  * Later: add metadata hooks + the data itself to define properties (again:
+    using the global flag to enable/disable properties on existing classes)
+
+  * The plan: flag == False in 2.3, flag == True in 2.4, remove flag in 2.5.
+    (and: add hooks to py2app to enable setting the flag in setup.py)
+
 - TODO: compile bridgesupport files into something more efficient, drop
   libxml support code (to because of libxml on 10.6 and 10.5 mess)
 
-  (At the very least least: the PyObjCMethodSignature objects can be shared more than
-  they are now).
+  (At the very least least: the PyObjCMethodSignature objects can be shared 
+  more than they are now). 
+
+  This needs further investigation to determine why we use more memory
+  than Ruby.
 
 - TODO: Drop support for 2.5 and 
 
@@ -84,7 +144,16 @@
   This should improve performance and reduce memory usage (when proper caching
   is used).
 
-- TODO: Rebuild documentation using sphynx (sp?)
+- TODO: Rebuild documentation using Sphinx
+
+  (In progress)
+
+- The (undocument) module ``PyObjCTools.DistUtilsSupport`` is no longer
+  present.
+
+- Converting a negative value to an unsigned integer now causes
+  a deprecation warning, this will be a hard error once I update
+  all framework wrapper metadata. 
 
 
 Version 2.2 (2009-11-24)

Modified: trunk/pyobjc/pyobjc-core/setup.py
==============================================================================
--- trunk/pyobjc/pyobjc-core/setup.py	(original)
+++ trunk/pyobjc/pyobjc-core/setup.py	Thu Jan 28 04:44:28 2010
@@ -37,7 +37,6 @@
         def run_tests(self):
             import sys, os
             rootdir =  os.path.dirname(os.path.abspath(__file__))
-            print ("*"*5, rootdir)
             if rootdir in sys.path:
                 sys.path.remove(rootdir)
             from PyObjCTest.loader import makeTestSuite

------------------------------------------------------------------------------
The Planet: dedicated and managed hosting, cloud storage, colocation
Stay online with enterprise data centers and the best network in the business
Choose flexible plans and management services without long-term contracts
Personal 24x7 support from experience hosting pros just a phone call away.
http://p.sf.net/sfu/theplanet-com