Commits

Ronald Oussoren committed 6078a11

- Fix a number of py2.5 issues in test cases
- disable osxcasts on py2.5, Carbon.CF is still broken
- "Private" classes are no longer imported when using objc.loadBundle
- "Private" methods are no longer visible in the class __dict__ (and hence not
for introspection tools).

"Private" names are names that start with an underscore. This removes a large
fraction of undocumented API's from our view, making introspection more useable.

  • Participants
  • Parent commits cf7e489
  • Branches pyobjc-ancient

Comments (0)

Files changed (10)

Lib/Foundation/test/test_osxcasts.py

 
 try:
     import sys
-    if sys.version_info[:2] == (2,4):
+    if sys.version_info[:2] in [(2,4), (2,5)]:
         raise ImportError, "py2.4 hangs when a CFArray is created, bug was filed"
 
     # These tests are only useful on MacOS X when using MacPython

Lib/objc/test/test_protected.py

+import unittest
+from objc.test.protected import *
+
+class TestProtected (unittest.TestCase):
+    def testProtectedNotInDir(self):
+
+        d = dir(PyObjCTest_Protected)
+        self.assert_( 'publicMethod' in d )
+        self.assert_( '_protectedMethod' not in d )
+
+    def testProtectedCallable(self):
+        o = PyObjCTest_Protected.new()
+        self.assertEquals(None, o._protectedMethod())
+        self.assertEquals(None, o.publicMethod())
+
+if __name__ == "__main__":
+    unittest.main()

Lib/objc/test/test_signatures.py

         s = objc.selector(lambda(x,y):1, signature="ii")
         self.assertEquals(s.native_signature, None)
 
-        self.assertRaises(TypeError, setattr, s, 'native_signature', 'v@:ii')
+        self.assertRaises((TypeError, AttributeError), setattr, s, 'native_signature', 'v@:ii')
 
         s = objc.lookUpClass('NSObject').description
-        self.assertRaises(TypeError, setattr, s, 'native_signature', 'v@:ii')
+        self.assertRaises((TypeError, AttributeError), setattr, s, 'native_signature', 'v@:ii')
 
         # We know that the description signature isn't changed by default
         self.assertEquals(s.signature, s.native_signature)

Modules/objc/module.m

 #endif
 
 int PyObjC_VerboseLevel = 0;
+int PyObjC_HideProtected = 1;
+
 PyObject* PyObjCClass_DefaultModule = NULL;
 PyObject* PyObjC_NSNumberWrapper = NULL;
 PyObject* PyObjCStrBridgeWarning = NULL;
 	return PyObjC_NSNumberWrapper;
 }
 
+PyDoc_STRVAR(setHideProtected_doc,
+	"setHideProtected(bool) -> None\n"
+	"\n"
+	"If true methods whose name starts with an underscore will not "
+	"visible for introspection using dir() or the class __dict__.");
+static PyObject* 
+setHideProtected(PyObject* self __attribute__((__unused__)), PyObject* args, PyObject* kwds)
+{
+static 	char* keywords[] = { "flag", NULL };
+	PyObject* o;
+
+	if (!PyArg_ParseTupleAndKeywords(args, kwds, "O:setVerbose",
+			keywords, &o)) {
+		return NULL;
+	}
+
+	PyObjC_HideProtected = PyObject_IsTrue(o);
+
+	Py_INCREF(Py_None);
+	return Py_None;
+}
+
 
 PyDoc_STRVAR(setVerbose_doc,
 	"setVerbose(bool) -> None\n"
 		PyObject* item;
 		PyObject* mod;
 		Class     cls;
-		char*  nm;
+		const char*  nm;
 
 		item = PyTuple_GET_ITEM(class_list, i);
 		if (item == NULL) {
 		
 		if (nm[0] == '%') {
 			/* skip, posed-as type */
+		} else if (PyObjC_HideProtected && nm[0] == '_') {
+			/* Skip private classes */
 		} else if (strcmp(nm, "Object") == 0 
 				|| strcmp(nm, "List") == 0
 				|| strcmp(nm, "Protocol") == 0) {
 	{ "setNSNumberWrapper", (PyCFunction)setNSNumberWrapper, METH_VARARGS|METH_KEYWORDS, setNSNumberWrapper_doc },
 	{ "getNSNumberWrapper", (PyCFunction)getNSNumberWrapper, METH_VARARGS|METH_KEYWORDS, getNSNumberWrapper_doc },
 	{ "setVerbose", (PyCFunction)setVerbose, METH_VARARGS|METH_KEYWORDS, setVerbose_doc },
+	{ "setHideProtected", (PyCFunction)setHideProtected, METH_VARARGS|METH_KEYWORDS, setHideProtected_doc },
 	{ "getVerbose", (PyCFunction)getVerbose, METH_VARARGS|METH_KEYWORDS, getVerbose_doc },
 	{ "pyobjc_id", (PyCFunction)pyobjc_id, METH_VARARGS|METH_KEYWORDS, pyobjc_id_doc },
 	{ "repythonify", (PyCFunction)repythonify, METH_VARARGS|METH_KEYWORDS, repythonify_doc },

Modules/objc/objc-class.h

  * @field generation   The value of PyObjC_MappingCount at the last time
  *                     the method-list was updated.
  * @field useKVO    should the class implement automatic KVO notifications?
+ * @field protectedMethods methods whose name starts with an underscore
  *
  * @discussion
  *      This struct is the type-object for on Objective-C class. It stores
  *
  *	We store the __del__ implementation here instead of in the type itself
  *	to ensure that our teardown code is correctly called.
- *
- *	NOTE: The additional fields are in a seperate struct when using Python
- *	2.2, because it is not possible to store the information in the type
- *	object itself.
  */
 typedef struct {
 	PyHeapTypeObject base;
 	int hasPythonImpl;
 	int generation;
 	int useKVO;
+	PyObject* protectedMethods;
 } PyObjCClassObject;
 
 extern PyObject* PyObjCClass_DefaultModule;

Modules/objc/objc-class.m

 
 PyObject* PyObjC_ClassExtender = NULL;
 
-static int add_class_fields(Class objc_class, PyObject* dict);
+static int add_class_fields(Class objc_class, PyObject* dict, PyObject* protDict);
 static int add_convenience_methods(Class cls, PyObject* type_dict);
 static int update_convenience_methods(PyObject* cls);
 
 	PyObject* delmethod;
 	PyObject* useKVOObj;
 	PyObjCRT_Ivar_t var;
+	PyObject* protectedMethods = NULL;
 
 	if (!PyArg_ParseTupleAndKeywords(args, kwds, "sOO:__new__",
 			keywords, &name, &bases, &dict)) {
 		PyObjCClass_CheckMethodList(py_super_class, 1);
 	}
 
+	protectedMethods = PyDict_New();
+	if (protectedMethods == NULL) {
+		return NULL;
+	}
+
 	/*
 	 * __pyobjc_protocols__ contains the list of protocols supported
 	 * by an existing class.
 	info->useKVO = 0;
 	info->delmethod = delmethod;
 	info->hasPythonImpl = 1;
+	info->protectedMethods = protectedMethods;
 
 	PyObjCClass_FinishClass(objc_class);
 
 
 			r = add_class_fields(
 				info->class,
-				((PyTypeObject*)cls)->tp_dict);
+				((PyTypeObject*)cls)->tp_dict,
+				info->protectedMethods);
 			if (r < 0) {
 				PyErr_SetString(PyExc_RuntimeError,
 					"Cannot rescan method table");
 }
 
 static PyGetSetDef class_getset[] = {
-		{
-				"pyobjc_classMethods",
-				(getter)cls_get_classMethods,
-				NULL,
-				cls_get_classMethods_doc,
-				0
-		},
-		{
-				"pyobjc_instanceMethods",
-				(getter)cls_get_instanceMethods,
-				NULL,
-				cls_get_instanceMethods_doc,
-				0
-		},
+	{
+		"pyobjc_classMethods",
+		(getter)cls_get_classMethods,
+		NULL,
+		cls_get_classMethods_doc,
+		0
+	},
+	{
+		"pyobjc_instanceMethods",
+		(getter)cls_get_instanceMethods,
+		NULL,
+		cls_get_instanceMethods_doc,
+		0
+	},
 	{
 		/* Access __name__ through a property: Objective-C name 
 		 * might change due to posing.
 		NULL,
 		0
 	},
-		{ 0, 0, 0, 0, 0 }
+	{ 0, 0, 0, 0, 0 }
 };
 
 static PyMemberDef class_members[] = {
  * surprising)
  */
 static int
-add_class_fields(Class objc_class, PyObject* dict)
+add_class_fields(Class objc_class, PyObject* pubDict, PyObject* protDict)
 {
 	Class     cls;
 	struct objc_method_list* mlist;
 	void*     iterator;
 	PyObject* descr;
 	char      selbuf[1024];
+	PyObject* dict;
 
 	if (objc_class == NULL) return 0;
 
 			char* name;
 			PyObject* curItem;
 
+
 			meth = mlist->method_list + i;
 
+			dict = pubDict;
+			if (*PyObjCRT_SELName(meth->method_name) == '_') {
+				if (PyObjC_HideProtected) {
+					dict = protDict;
+				}
+			}
 			name = (char*)PyObjC_SELToPythonName(
 						meth->method_name, 
 						selbuf, 
 		for (i = 0; i < mlist->method_count; i++) {
 			meth = mlist->method_list + i;
 
+			dict = pubDict;
+			if (*PyObjCRT_SELName(meth->method_name) == '_') {
+				if (PyObjC_HideProtected) {
+					dict = protDict;
+				}
+			}
+
 			PyObjC_SELToPythonName(
 				meth->method_name, 
 				selbuf, 
 	PyObject* bases;
 	PyObjCClassObject* info;
 	PyObjCRT_Ivar_t var;
+	PyObject* protectedMethods;
 
 	result = objc_class_locate(objc_class);
 	if (result != NULL) {
 		return result;
 	}
 
+	protectedMethods = PyDict_New();
+	if (protectedMethods == NULL) {
+		return NULL;
+	}
+
 #ifdef GNU_RUNTIME
 	/*
 	 * FIXME: we do get unresolved classes when fetching the class list
 	info->useKVO = 0;
 	info->delmethod = NULL;
 	info->hasPythonImpl = 0;
+	info->protectedMethods = protectedMethods;
 
 	/* XXX: Hack to support buffer API */
 	if (strcmp(objc_class->name, "NSData") == 0) {

Modules/objc/objc-object.m

 	int i, n;
 	PyObject *mro, *base, *dict;
 	PyObject *descr = NULL;
+	PyObject* protDict;
 
 	/* Look in tp_dict of types in MRO */
 	mro = tp->tp_mro;
 
 		if (PyClass_Check(base)) {
 			dict = ((PyClassObject*)base)->cl_dict;
+			protDict = NULL;
 		} else {
 			assert(PyType_Check(base));
+			protDict = NULL;
 			if (PyObjCClass_Check(base)) {
 				PyObjCClass_CheckMethodList(base, 0);
+				protDict = ((PyObjCClassObject*)base)->protectedMethods;
 			}
 
 			dict = ((PyTypeObject *)base)->tp_dict;
 		if (descr != NULL) {
 			break;
 		}
+
+		if (protDict) {
+			descr = PyDict_GetItem(protDict, name);
+			if (descr != NULL) {
+				break;
+			}
+		}
 	}
 
 	return descr;
      { 0, 0, 0, 0 },			/* as_buffer */
      0,					/* name */
      0,					/* slots */
-   }, 0, 0, 0, 0, 0, 0, 0, 0
+   }, 0, 0, 0, 0, 0, 0, 0, 0, 0
 };
 
 /*

Modules/objc/pyobjc.h

 #endif
 
 extern int PyObjC_VerboseLevel;
+extern int PyObjC_HideProtected;
 extern int PyObjC_StrBridgeEnabled;
 extern PyObject *PyObjCStrBridgeWarning;
 extern PyObject *PyObjC_NSNumberWrapper;

Modules/objc/test/protected.m

+#include "Python.h"
+#include "pyobjc-api.h"
+
+#import <Foundation/Foundation.h>
+
+@interface PyObjCTest_Protected : NSObject
+{}
+-publicMethod;
+-_protectedMethod;
+@end
+
+@implementation PyObjCTest_Protected 
+-publicMethod
+{
+	return nil;
+}
+
+-_protectedMethod
+{
+	return nil;
+}
+@end
+
+static PyMethodDef mod_methods[] = {
+	        { 0, 0, 0, 0 }
+};
+
+void initprotected(void);
+void initprotected(void)
+{
+	PyObject* m;
+
+	m = Py_InitModule4("protected", mod_methods, NULL, NULL, PYTHON_API_VERSION);
+
+	PyObjC_ImportAPI(m);
+
+	PyModule_AddObject(m, "PyObjCTest_Protected", 
+		PyObjCClass_New([PyObjCTest_Protected class]));
+}
+
 Version 1.3.8 (????-??-??)
 --------------------------
 
+- Classes whose name starts with and underscore are no longer imported when
+  using ``objc.loadBundle``. They are still available using 
+  ``objc.lookUpClass``.
+
+  Methods whose name starts with an underscore are no longer visible when 
+  introspecting using ``dir()``, but can still be called. 
+
+  These changes were done to make introspection slightly more user-friendly:
+  anything that is now hidden from introspection is most likely not part of
+  a public API.
+
 - Most of the libffi testsuite is now run using a module that emulates 
   dejagnu.