Commits

Ronald Oussoren  committed ed7020e

Add an __signature__ property to objc.function and objc.selector objects

The __signature__ is an inspect.Signature object and currently contains
minimal information, the information can be enhanced later on by
changing the code in objc._callable_docstr.

In the long run this should make the output of pydoc nicer for PyObjC
callables, that requires a change to the Python stdlib though (because
pydoc currently does not use inspect.signature) and therefore won't
happen before Python 3.4.

  • Participants
  • Parent commits 03a96e4

Comments (0)

Files changed (4)

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

 __all__ = ()
 from objc._objc import _setCallableDoc, _nameForSignature
+import sys
 import objc
 
 basic_types = {
     # XXX: handle struct, union, array, bitfield
     return "<?>"
 
-def describe_signature(callable):
+def describe_callable(callable):
     name     = callable.__name__
     metadata = callable.__metadata__()
 
-    return describe_callable(name, metadata, ismethod=isinstance(callable, objc.selector))
+    return describe_callable_metadata(name, metadata, ismethod=isinstance(callable, objc.selector))
 
-def describe_callable(name, metadata, offset='', ismethod=False):
+def describe_callable_metadata(name, metadata, offset='', ismethod=False):
     arg_info = []
     if ismethod:
         arg_offset = 2
                 continue
 
             elif info.get('callable'):
-                result.append('arg%d: %s'%(idx, describe_callable('callback', info['callable'], offset='    ' + offset)))
+                result.append('arg%d: %s'%(idx, describe_callable_metadata('callback', info['callable'], offset='    ' + offset)))
                 continue
 
             else:
 
     return ('\n'+offset).join(result)
 
-_setCallableDoc(describe_signature)
+_setCallableDoc(describe_callable)
+
+if sys.version_info[:2] >= (3,3):
+    import inspect
+
+    def callable_signature(callable):
+        # Create an inspect.Signature for an PyObjC callable
+        # both objc.function and objc.native_selector only support positional 
+        # arguments, and not keyword arguments.
+        #
+        # TODO: it might be useful to add annotations when the argument/result
+        #       value is not an object.
+        metadata = callable.__metadata__()
+        ismethod = isinstance(callable, objc.selector)
+
+        if ismethod:
+            args = metadata['arguments'][2:] # Skip 'self' and 'selector' implicit arguments
+        else:
+            args = metadata['arguments']
+
+        parameters = []
+        for idx, arg in enumerate(args):
+            p_name = 'arg%d'%(idx,)
+            parameters.append(
+                inspect.Parameter(
+                    p_name,
+                    inspect.Parameter.POSITIONAL_ONLY
+                )
+            )
+        
+        return inspect.Signature(parameters)
+
+    objc._setCallableSignature(callable_signature)

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

 	{ 0, 0, 0, 0 }
 };
 
-PyDoc_STRVAR(func_docstring_doc, "The document string for a method");
 static PyGetSetDef func_getset[] = {
 	{
 		"__doc__",
 		PyObjC_callable_docstr_get,
 		0,
-		func_docstring_doc,
+		"Documentation for a function",
 		0
 	},
+#if PY_VERSION_HEX >= 0x03030000
+	{
+		"__signature__",
+		PyObjC_callable_signature_get,
+		0,
+		"inspect.Signature for a function",
+		0
+	},
+#endif
 	{ 0, 0, 0, 0, 0 }
 };
 

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

 	return Py_None;
 }
 
+
+#if PY_VERSION_HEX >= 0x03030000
+static PyObject* callable_signature_function = NULL;
+
+PyObject* PyObjC_callable_signature_get(PyObject* callable, void* closure __attribute__((__unused__)))
+
+{
+	if (callable_signature_function == NULL) {
+		Py_INCREF(Py_None);
+		return Py_None;
+	}
+	return PyObject_CallFunction(callable_signature_function, "O", callable);
+}
+
+
+static PyObject* 
+set_callable_signature(PyObject* mod __attribute__((__unused__)), PyObject* hook)
+{
+	Py_INCREF(hook);
+	Py_XDECREF(callable_signature_function);
+	callable_signature_function = hook;
+	Py_INCREF(Py_None);
+	return Py_None;
+}
+#endif
+
 static PyObject*
 name_for_signature(PyObject* mod __attribute__((__unused__)), PyObject* signature)
 {
 		METH_VARARGS|METH_KEYWORDS, "(PRIVATE)" },
 	{ "_setCallableDoc", (PyCFunction)set_callable_doc,
 		METH_O, "private function" },
+#if PY_VERSION_HEX >= 0x03030000
+	{ "_setCallableSignature", (PyCFunction)set_callable_signature,
+		METH_O, "private function" },
+#endif
 	{ "_nameForSignature", (PyCFunction)name_for_signature,
 		METH_O, "private function" },
 
 		PyObjC_INITERROR();
 	}
 
+	if (PyObjC_setup_nsdecimal(m) < 0) {
+		PyObjC_INITERROR();
+	}
+
 
 	d = PyModule_GetDict(m);
 	if (d == 0) {

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

 	return (PyObject*)result;
 }
 
-PyDoc_STRVAR(objcsel_docstring_doc, "The document string for a method");
 static PyGetSetDef objcsel_getset[] = {
 	{
 		"__doc__",
 		PyObjC_callable_docstr_get,
 		0,
-		objcsel_docstring_doc,
+		"The document string for a method",
 		0
 	},
+#if PY_VERSION_HEX >= 0x03030000
+	{
+		"__signature__",
+		PyObjC_callable_signature_get,
+		0,
+		"inspect.Signature for a method",
+		0
+	},
+#endif
 	{ 0, 0, 0, 0, 0 }
 };
 
 	return docstr;
 }
 
+PyDoc_STRVAR(pysel_signature_doc,
+	"inspect.Signaturefor a method");
+
 static PyGetSetDef pysel_getset[] = {
 	{
 		"callable",
 		pysel_docstring_doc,
 		0
 	},
+	{
+		"__signature__",
+		PyObjC_callable_signature_get,
+		0,
+		pysel_signature_doc,
+		0
+	},
 
 	{
 		NULL,