[pygobject] Add doc strings showing method signatures for gi methods
- From: Simon Feltman <sfeltman src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [pygobject] Add doc strings showing method signatures for gi methods
- Date: Mon, 12 Nov 2012 07:03:15 +0000 (UTC)
commit 13629f5a9c9a7022f3521a3616d9ce8fa4a6161b
Author: Simon Feltman <s feltman gmail com>
Date: Thu Aug 16 15:09:08 2012 -0700
Add doc strings showing method signatures for gi methods
Add signature based doc string to all methods pulled in from
introspection. For example: Gtk.SpinButton.get_icon_area.__doc__
get_icon_area(self, icon_pos:Gtk.EntryIconPosition) -> icon_area:cairo.RectangleInt
https://bugzilla.gnome.org/show_bug.cgi?id=681967
gi/overrides/GIMarshallingTests.py | 1 +
gi/pygi-info.c | 103 ++++++++++++++++++++++++++++++++---
gi/pygi-type.c | 60 +++++++++++++++++++++
gi/pygi-type.h | 1 +
gi/types.py | 92 +++++++++++++++++++++++++-------
tests/test_gi.py | 30 ++++++++++
6 files changed, 259 insertions(+), 28 deletions(-)
---
diff --git a/gi/overrides/GIMarshallingTests.py b/gi/overrides/GIMarshallingTests.py
index af2529a..cc967b4 100644
--- a/gi/overrides/GIMarshallingTests.py
+++ b/gi/overrides/GIMarshallingTests.py
@@ -63,6 +63,7 @@ class OverridesObject(GIMarshallingTests.OverridesObject):
return self
def method(self):
+ """Overridden doc string."""
return GIMarshallingTests.OverridesObject.method(self) / 7
OverridesObject = override(OverridesObject)
diff --git a/gi/pygi-info.c b/gi/pygi-info.c
index dc99a83..e726b2d 100644
--- a/gi/pygi-info.c
+++ b/gi/pygi-info.c
@@ -357,13 +357,91 @@ static PyMethodDef _PyGIPropertyInfo_methods[] = {
{ NULL, NULL, 0 }
};
+
/* ArgInfo */
PYGLIB_DEFINE_TYPE ("gi.ArgInfo", PyGIArgInfo_Type, PyGIBaseInfo);
+static PyObject *
+_wrap_g_arg_info_get_direction (PyGIBaseInfo *self)
+{
+ return PyLong_FromLong (
+ g_arg_info_get_direction ((GIArgInfo*)self->info) );
+}
+
+static PyObject *
+_wrap_g_arg_info_is_caller_allocates (PyGIBaseInfo *self)
+{
+ return PyBool_FromLong (
+ g_arg_info_is_caller_allocates ((GIArgInfo*)self->info) );
+}
+
+static PyObject *
+_wrap_g_arg_info_is_return_value (PyGIBaseInfo *self)
+{
+ return PyBool_FromLong (
+ g_arg_info_is_return_value ((GIArgInfo*)self->info) );
+}
+
+static PyObject *
+_wrap_g_arg_info_is_optional (PyGIBaseInfo *self)
+{
+ return PyBool_FromLong (
+ g_arg_info_is_optional ((GIArgInfo*)self->info) );
+}
+
+static PyObject *
+_wrap_g_arg_info_may_be_null (PyGIBaseInfo *self)
+{
+ return PyBool_FromLong (
+ g_arg_info_may_be_null ((GIArgInfo*)self->info) );
+}
+
+/* _g_arg_get_pytype_hint
+ *
+ * Returns new value reference to a string hinting at the python type
+ * which can be used for the given gi argument info.
+ */
+static PyObject *
+_g_arg_get_pytype_hint (PyGIBaseInfo *self)
+{
+ GIArgInfo *arg_info = (GIArgInfo*)self->info;
+ GITypeInfo type_info;
+ g_arg_info_load_type(arg_info, &type_info);
+ GITypeTag type_tag = g_type_info_get_tag(&type_info);
+
+ /* First attempt getting a python type object. */
+ PyObject *py_type = _pygi_get_py_type_hint(type_tag);
+ if (py_type != Py_None && PyObject_HasAttrString(py_type, "__name__")) {
+ PyObject *name = PyObject_GetAttrString(py_type, "__name__");
+ Py_DecRef(py_type);
+ return name;
+ } else {
+ Py_DecRef(py_type);
+ if (type_tag == GI_TYPE_TAG_INTERFACE) {
+ GIBaseInfo *iface = g_type_info_get_interface(&type_info);
+ gchar *name = g_strdup_printf("%s.%s",
+ g_base_info_get_namespace(iface),
+ g_base_info_get_name (iface));
+ g_base_info_unref(iface);
+ PyObject *py_string = PYGLIB_PyUnicode_FromString(name);
+ g_free(name);
+ return py_string;
+ }
+ return PYGLIB_PyUnicode_FromString(g_type_tag_to_string(type_tag));
+ }
+}
+
static PyMethodDef _PyGIArgInfo_methods[] = {
+ { "get_direction", (PyCFunction) _wrap_g_arg_info_get_direction, METH_NOARGS },
+ { "is_caller_allocates", (PyCFunction) _wrap_g_arg_info_is_caller_allocates, METH_NOARGS },
+ { "is_return_value", (PyCFunction) _wrap_g_arg_info_is_return_value, METH_NOARGS },
+ { "is_optional", (PyCFunction) _wrap_g_arg_info_is_optional, METH_NOARGS },
+ { "may_be_null", (PyCFunction) _wrap_g_arg_info_may_be_null, METH_NOARGS },
+ { "get_pytype_hint", (PyCFunction) _g_arg_get_pytype_hint, METH_NOARGS },
{ NULL, NULL, 0 }
};
+
/* TypeInfo */
PYGLIB_DEFINE_TYPE ("gi.TypeInfo", PyGITypeInfo_Type, PyGIBaseInfo);
@@ -1644,27 +1722,34 @@ _pygi_info_register_types (PyObject *m)
if (PyModule_AddObject(m, "BaseInfo", (PyObject *)&PyGIBaseInfo_Type))
return;
+ if (PyModule_AddObject(m, "DIRECTION_IN", PyLong_FromLong(GI_DIRECTION_IN)))
+ return;
+ if (PyModule_AddObject(m, "DIRECTION_OUT", PyLong_FromLong(GI_DIRECTION_OUT)))
+ return;
+ if (PyModule_AddObject(m, "DIRECTION_INOUT", PyLong_FromLong(GI_DIRECTION_INOUT)))
+ return;
+
_PyGI_REGISTER_TYPE (m, PyGIUnresolvedInfo_Type, UnresolvedInfo,
PyGIBaseInfo_Type);
- _PyGI_REGISTER_TYPE (m, PyGICallableInfo_Type, CallableInfo,
+ _PyGI_REGISTER_TYPE (m, PyGICallableInfo_Type, CallableInfo,
PyGIBaseInfo_Type);
_PyGI_REGISTER_TYPE (m, PyGICallbackInfo_Type, CallbackInfo,
PyGIBaseInfo_Type);
- _PyGI_REGISTER_TYPE (m, PyGIFunctionInfo_Type, FunctionInfo,
+ _PyGI_REGISTER_TYPE (m, PyGIFunctionInfo_Type, FunctionInfo,
PyGICallableInfo_Type);
- _PyGI_REGISTER_TYPE (m, PyGIRegisteredTypeInfo_Type, RegisteredTypeInfo,
+ _PyGI_REGISTER_TYPE (m, PyGIRegisteredTypeInfo_Type, RegisteredTypeInfo,
PyGIBaseInfo_Type);
- _PyGI_REGISTER_TYPE (m, PyGIStructInfo_Type, StructInfo,
+ _PyGI_REGISTER_TYPE (m, PyGIStructInfo_Type, StructInfo,
PyGIRegisteredTypeInfo_Type);
- _PyGI_REGISTER_TYPE (m, PyGIEnumInfo_Type, EnumInfo,
+ _PyGI_REGISTER_TYPE (m, PyGIEnumInfo_Type, EnumInfo,
PyGIRegisteredTypeInfo_Type);
- _PyGI_REGISTER_TYPE (m, PyGIObjectInfo_Type, ObjectInfo,
+ _PyGI_REGISTER_TYPE (m, PyGIObjectInfo_Type, ObjectInfo,
PyGIRegisteredTypeInfo_Type);
- _PyGI_REGISTER_TYPE (m, PyGIInterfaceInfo_Type, InterfaceInfo,
+ _PyGI_REGISTER_TYPE (m, PyGIInterfaceInfo_Type, InterfaceInfo,
PyGIRegisteredTypeInfo_Type);
- _PyGI_REGISTER_TYPE (m, PyGIConstantInfo_Type, ConstantInfo,
+ _PyGI_REGISTER_TYPE (m, PyGIConstantInfo_Type, ConstantInfo,
PyGIBaseInfo_Type);
- _PyGI_REGISTER_TYPE (m, PyGIValueInfo_Type, ValueInfo,
+ _PyGI_REGISTER_TYPE (m, PyGIValueInfo_Type, ValueInfo,
PyGIBaseInfo_Type);
_PyGI_REGISTER_TYPE (m, PyGIFieldInfo_Type, FieldInfo,
PyGIBaseInfo_Type);
diff --git a/gi/pygi-type.c b/gi/pygi-type.c
index 129ea98..dfaadb0 100644
--- a/gi/pygi-type.c
+++ b/gi/pygi-type.c
@@ -23,6 +23,8 @@
#include "pygi-private.h"
+#include <pyglib-python-compat.h>
+
PyObject *
_pygi_type_import_by_name (const char *namespace_,
@@ -97,3 +99,61 @@ _pygi_type_get_from_g_type (GType g_type)
return py_type;
}
+/* _pygi_get_py_type_hint
+ *
+ * This gives a hint to what python type might be used as
+ * a particular gi type.
+ */
+PyObject *
+_pygi_get_py_type_hint(GITypeTag type_tag)
+{
+ PyObject *type = Py_None;
+
+ switch (type_tag) {
+ case GI_TYPE_TAG_BOOLEAN:
+ type = (PyObject *) &PyBool_Type;
+ break;
+
+ case GI_TYPE_TAG_INT8:
+ case GI_TYPE_TAG_UINT8:
+ case GI_TYPE_TAG_INT16:
+ case GI_TYPE_TAG_UINT16:
+ case GI_TYPE_TAG_INT32:
+ case GI_TYPE_TAG_UINT32:
+ case GI_TYPE_TAG_INT64:
+ case GI_TYPE_TAG_UINT64:
+ type = (PyObject *) &PYGLIB_PyLong_Type;
+ break;
+
+ case GI_TYPE_TAG_FLOAT:
+ case GI_TYPE_TAG_DOUBLE:
+ type = (PyObject *) &PyFloat_Type;
+ break;
+
+ case GI_TYPE_TAG_GLIST:
+ case GI_TYPE_TAG_GSLIST:
+ case GI_TYPE_TAG_ARRAY:
+ type = (PyObject *) &PyList_Type;
+ break;
+
+ case GI_TYPE_TAG_GHASH:
+ type = (PyObject *) &PyDict_Type;
+ break;
+
+ case GI_TYPE_TAG_UTF8:
+ case GI_TYPE_TAG_FILENAME:
+ case GI_TYPE_TAG_UNICHAR:
+ type = (PyObject *) &PYGLIB_PyUnicode_Type;
+ break;
+
+ case GI_TYPE_TAG_INTERFACE:
+ case GI_TYPE_TAG_GTYPE:
+ case GI_TYPE_TAG_ERROR:
+ case GI_TYPE_TAG_VOID:
+ break;
+ }
+
+ Py_INCREF(type);
+ return type;
+}
+
diff --git a/gi/pygi-type.h b/gi/pygi-type.h
index bb43d19..01b5994 100644
--- a/gi/pygi-type.h
+++ b/gi/pygi-type.h
@@ -39,6 +39,7 @@ PyObject *_pygi_type_import_by_gi_info (GIBaseInfo *info);
PyObject *_pygi_type_get_from_g_type (GType g_type);
+PyObject *_pygi_get_py_type_hint (GITypeTag type_tag);
G_END_DECLS
diff --git a/gi/types.py b/gi/types.py
index af3310a..a7e9e4d 100644
--- a/gi/types.py
+++ b/gi/types.py
@@ -23,6 +23,7 @@
from __future__ import absolute_import
import sys
+
from . import _gobject
from ._gobject._gobject import GInterface
from ._gobject.constants import TYPE_INVALID
@@ -32,55 +33,108 @@ from ._gi import \
ObjectInfo, \
StructInfo, \
VFuncInfo, \
+ FunctionInfo, \
register_interface_info, \
- hook_up_vfunc_implementation
+ hook_up_vfunc_implementation, \
+ DIRECTION_IN, \
+ DIRECTION_OUT, \
+ DIRECTION_INOUT
StructInfo # pyflakes
-if sys.version_info > (3, 0):
+if (3, 0) <= sys.version_info < (3, 3):
+ # callable not available for python 3.0 thru 3.2
def callable(obj):
return hasattr(obj, '__call__')
-def Function(info):
+def split_function_info_args(info):
+ """Split a functions args into a tuple of two lists.
+
+ Note that args marked as DIRECTION_INOUT will be in both lists.
+
+ :Returns:
+ Tuple of (in_args, out_args)
+ """
+ in_args = []
+ out_args = []
+ for arg in info.get_arguments():
+ direction = arg.get_direction()
+ if direction in (DIRECTION_IN, DIRECTION_INOUT):
+ in_args.append(arg)
+ if direction in (DIRECTION_OUT, DIRECTION_INOUT):
+ out_args.append(arg)
+ return (in_args, out_args)
+
+
+def get_callable_info_doc_string(info):
+ """Build a signature string which can be used for documentation."""
+ in_args, out_args = split_function_info_args(info)
+ in_args_strs = []
+ if isinstance(info, VFuncInfo):
+ in_args_strs = ['self']
+ elif isinstance(info, FunctionInfo):
+ if info.is_method():
+ in_args_strs = ['self']
+ elif info.is_constructor():
+ in_args_strs = ['cls']
+
+ for arg in in_args:
+ argstr = arg.get_name() + ':' + arg.get_pytype_hint()
+ if arg.is_optional():
+ argstr += '=<optional>'
+ in_args_strs.append(argstr)
+ in_args_str = ', '.join(in_args_strs)
+
+ if out_args:
+ out_args_str = ', '.join(arg.get_name() + ':' + arg.get_pytype_hint() \
+ for arg in out_args)
+ return '%s(%s) -> %s' % (info.get_name(), in_args_str, out_args_str)
+ else:
+ return '%s(%s)' % (info.get_name(), in_args_str)
+
+
+def wraps_callable_info(info):
+ """Similar to functools.wraps but with specific GICallableInfo support."""
+ def update_func(func):
+ func.__info__ = info
+ func.__name__ = info.get_name()
+ func.__module__ = info.get_namespace()
+ func.__doc__ = get_callable_info_doc_string(info)
+ return func
+ return update_func
+
+def Function(info):
+ """Warps GIFunctionInfo"""
+ @wraps_callable_info(info)
def function(*args, **kwargs):
return info.invoke(*args, **kwargs)
- function.__info__ = info
- function.__name__ = info.get_name()
- function.__module__ = info.get_namespace()
return function
class NativeVFunc(object):
-
+ """Wraps GINativeVFuncInfo"""
def __init__(self, info):
- self._info = info
+ self.__info__ = info
def __get__(self, instance, klass):
+ @wraps_callable_info(self.__info__)
def native_vfunc(*args, **kwargs):
- return self._info.invoke(klass.__gtype__, *args, **kwargs)
- native_vfunc.__info__ = self._info
- native_vfunc.__name__ = self._info.get_name()
- native_vfunc.__module__ = self._info.get_namespace()
-
+ return self.__info__.invoke(klass.__gtype__, *args, **kwargs)
return native_vfunc
def Constructor(info):
-
+ """Warps GIFunctionInfo with get_constructor() == True"""
+ @wraps_callable_info(info)
def constructor(cls, *args, **kwargs):
cls_name = info.get_container().get_name()
if cls.__name__ != cls_name:
raise TypeError('%s constructor cannot be used to create instances of a subclass' % cls_name)
return info.invoke(cls, *args, **kwargs)
-
- constructor.__info__ = info
- constructor.__name__ = info.get_name()
- constructor.__module__ = info.get_namespace()
-
return constructor
diff --git a/tests/test_gi.py b/tests/test_gi.py
index f65cf74..76f4c3d 100644
--- a/tests/test_gi.py
+++ b/tests/test_gi.py
@@ -2601,3 +2601,33 @@ class TestObjectInfo(unittest.TestCase):
repo = gi.gi.Repository.get_default()
info = repo.find_by_name('GObject', 'Object')
self.assertFalse(info.get_abstract())
+
+
+class TestSignatureArgs(unittest.TestCase):
+ def test_split_args_multi_out(self):
+ in_args, out_args = gi.types.split_function_info_args(GIMarshallingTests.int_out_out.__info__)
+ self.assertEqual(len(in_args), 0)
+ self.assertEqual(len(out_args), 2)
+ self.assertEqual(out_args[0].get_pytype_hint(), 'int')
+ self.assertEqual(out_args[1].get_pytype_hint(), 'int')
+
+ def test_split_args_inout(self):
+ in_args, out_args = gi.types.split_function_info_args(GIMarshallingTests.long_inout_max_min.__info__)
+ self.assertEqual(len(in_args), 1)
+ self.assertEqual(len(out_args), 1)
+ self.assertEqual(in_args[0].get_name(), out_args[0].get_name())
+ self.assertEqual(in_args[0].get_pytype_hint(), out_args[0].get_pytype_hint())
+
+ def test_split_args_none(self):
+ obj = GIMarshallingTests.Object(int=33)
+ in_args, out_args = gi.types.split_function_info_args(obj.none_inout.__info__)
+ self.assertEqual(len(in_args), 1)
+ self.assertEqual(len(out_args), 1)
+
+ def test_final_signature_with_full_inout(self):
+ self.assertEqual(GIMarshallingTests.Object.full_inout.__doc__,
+ 'full_inout(object:GIMarshallingTests.Object) -> object:GIMarshallingTests.Object')
+
+ def test_overridden_doc_is_not_clobbered(self):
+ self.assertEqual(GIMarshallingTests.OverridesObject.method.__doc__,
+ 'Overridden doc string.')
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]