diff options
Diffstat (limited to 'gi/pygi-info.c')
-rw-r--r-- | gi/pygi-info.c | 290 |
1 files changed, 276 insertions, 14 deletions
diff --git a/gi/pygi-info.c b/gi/pygi-info.c index 3e8fcb8..7164ff9 100644 --- a/gi/pygi-info.c +++ b/gi/pygi-info.c @@ -27,6 +27,33 @@ #include <pygobject.h> #include <pyglib-python-compat.h> + +/* _generate_doc_string + * + * C wrapper to call Python implemented "gi.docstring.generate_doc_string" + */ +static PyObject * +_generate_doc_string(PyGIBaseInfo *self) +{ + static PyObject *_py_generate_doc_string = NULL; + + if (_py_generate_doc_string == NULL) { + PyObject *mod = PyImport_ImportModule ("gi.docstring"); + if (!mod) + return NULL; + + _py_generate_doc_string = PyObject_GetAttrString (mod, "generate_doc_string"); + if (_py_generate_doc_string == NULL) { + Py_DECREF (mod); + return NULL; + } + Py_DECREF (mod); + } + + return PyObject_CallFunctionObjArgs (_py_generate_doc_string, self, NULL); +} + + /* BaseInfo */ static void @@ -170,6 +197,55 @@ static PyMethodDef _PyGIBaseInfo_methods[] = { { NULL, NULL, 0 } }; +/* _base_info_getattro: + * + * The usage of __getattr__ is needed because the get/set method table + * does not work for __doc__. + */ +static PyObject * +_base_info_getattro(PyGIBaseInfo *self, PyObject *name) +{ + PyObject *result; + + static PyObject *docstr; + if (docstr == NULL) { + docstr= PYGLIB_PyUnicode_InternFromString("__doc__"); + if (docstr == NULL) + return NULL; + } + + Py_INCREF (name); + PYGLIB_PyUnicode_InternInPlace (&name); + + if (name == docstr) { + result = _generate_doc_string (self); + } else { + result = PyObject_GenericGetAttr ((PyObject *)self, name); + } + + Py_DECREF (name); + return result; +} + +static PyObject * +_base_info_attr_name(PyGIBaseInfo *self, void *closure) +{ + return _wrap_g_base_info_get_name (self); +} + +static PyObject * +_base_info_attr_module(PyGIBaseInfo *self, void *closure) +{ + return PYGLIB_PyUnicode_FromFormat ("gi.repository.%s", + g_base_info_get_namespace (self->info)); +} + +static PyGetSetDef _base_info_getsets[] = { + { "__name__", (getter)_base_info_attr_name, (setter)0, "Name", NULL}, + { "__module__", (getter)_base_info_attr_module, (setter)0, "Module name", NULL}, + { NULL, 0, 0 } +}; + PyObject * _pygi_info_new (GIBaseInfo *info) { @@ -281,7 +357,7 @@ out: /* CallableInfo */ -PYGLIB_DEFINE_TYPE ("gi.CallableInfo", PyGICallableInfo_Type, PyGIBaseInfo); +PYGLIB_DEFINE_TYPE ("gi.CallableInfo", PyGICallableInfo_Type, PyGICallableInfo); static PyObject * _wrap_g_callable_info_get_arguments (PyGIBaseInfo *self) @@ -319,6 +395,183 @@ _wrap_g_callable_info_get_arguments (PyGIBaseInfo *self) return infos; } + +/* _callable_info_call: + * + * Shared wrapper for invoke which can be bound (instance method or class constructor) + * or unbound (function or static method). + */ +static PyObject * +_callable_info_call (PyGICallableInfo *self, PyObject *args, PyObject *kwargs) +{ + /* Insert the bound arg at the beginning of the invoke method args. */ + if (self->py_bound_arg) { + int i; + PyObject *result; + Py_ssize_t argcount = PyTuple_Size (args); + PyObject *newargs = PyTuple_New (argcount + 1); + if (newargs == NULL) + return NULL; + + Py_INCREF (self->py_bound_arg); + PyTuple_SET_ITEM (newargs, 0, self->py_bound_arg); + + for (i = 0; i < argcount; i++) { + PyObject *v = PyTuple_GET_ITEM (args, i); + Py_XINCREF (v); + PyTuple_SET_ITEM (newargs, i+1, v); + } + + /* Invoke with the original GI info struct this wrapper was based upon. + * This is necessary to maintain the same cache for all bound versions. + */ + result = _wrap_g_callable_info_invoke ((PyGIBaseInfo *)self->py_unbound_info, + newargs, kwargs); + Py_DECREF (newargs); + return result; + + } else { + /* We should never have an unbound info when calling when calling invoke + * at this point because the descriptor implementation on sub-classes + * should return "self" not a copy when there is no bound arg. + */ + g_assert (self->py_unbound_info == NULL); + return _wrap_g_callable_info_invoke ((PyGIBaseInfo *)self, args, kwargs); + } +} + + +/* _function_info_call: + * + * Specialization of _callable_info_call for GIFunctionInfo which + * handles constructor error conditions. + */ +static PyObject * +_function_info_call (PyGICallableInfo *self, PyObject *args, PyObject *kwargs) +{ + if (self->py_bound_arg) { + GIFunctionInfoFlags flags; + + /* Ensure constructors are only called as class methods on the class + * implementing the constructor and not on sub-classes. + */ + flags = g_function_info_get_flags ( (GIFunctionInfo*) self->base.info); + if (flags & GI_FUNCTION_IS_CONSTRUCTOR) { + PyObject *py_str_name; + const gchar *str_name; + GIBaseInfo *container_info = g_base_info_get_container (self->base.info); + g_assert (container_info != NULL); + + py_str_name = PyObject_GetAttrString (self->py_bound_arg, "__name__"); + if (py_str_name == NULL) + return NULL; + + if (PyUnicode_Check (py_str_name) ) { + PyObject *tmp = PyUnicode_AsUTF8String (py_str_name); + Py_DECREF (py_str_name); + py_str_name = tmp; + } + +#if PY_VERSION_HEX < 0x03000000 + str_name = PyString_AsString (py_str_name); +#else + str_name = PyBytes_AsString (py_str_name); +#endif + + if (strcmp (str_name, g_base_info_get_name (container_info))) { + PyErr_Format (PyExc_TypeError, + "%s constructor cannot be used to create instances of " + "a subclass %s", + g_base_info_get_name (container_info), + str_name); + Py_DECREF (py_str_name); + return NULL; + } + Py_DECREF (py_str_name); + } + } + + return _callable_info_call (self, args, kwargs); +} + +/* _new_bound_callable_info + * + * Utility function for sub-classes to create a bound version of themself. + */ +static PyGICallableInfo * +_new_bound_callable_info (PyGICallableInfo *self, PyObject *bound_arg) +{ + PyGICallableInfo *new_self; + + /* Return self if this is already bound or there is nothing passed to bind. */ + if (self->py_bound_arg != NULL || bound_arg == NULL || bound_arg == Py_None) { + Py_INCREF ((PyObject *)self); + return self; + } + + new_self = (PyGICallableInfo *)_pygi_info_new (self->base.info); + if (new_self == NULL) + return NULL; + + Py_INCREF ((PyObject *)self); + new_self->py_unbound_info = (struct PyGICallableInfo *)self; + + Py_INCREF (bound_arg); + new_self->py_bound_arg = bound_arg; + + return new_self; +} + +/* _function_info_descr_get + * + * Descriptor protocol implementation for functions, methods, and constructors. + */ +static PyObject * +_function_info_descr_get (PyGICallableInfo *self, PyObject *obj, PyObject *type) { + GIFunctionInfoFlags flags; + PyObject *bound_arg = NULL; + + flags = g_function_info_get_flags ( (GIFunctionInfo*) self->base.info); + if (flags & GI_FUNCTION_IS_CONSTRUCTOR) { + if (type == NULL) + bound_arg = (PyObject *)(Py_TYPE(obj)); + else + bound_arg = type; + } else if (flags & GI_FUNCTION_IS_METHOD) { + bound_arg = obj; + } + + return (PyObject *)_new_bound_callable_info (self, bound_arg); +} + +/* _vfunc_info_descr_get + * + * Descriptor protocol implementation for virtual functions. + */ +static PyObject * +_vfunc_info_descr_get (PyGICallableInfo *self, PyObject *obj, PyObject *type) { + PyObject *result; + PyObject *bound_arg = NULL; + + bound_arg = PyObject_GetAttrString (type, "__gtype__"); + if (bound_arg == NULL) + return NULL; + + /* _new_bound_callable_info adds its own ref so free the one from GetAttrString */ + result = (PyObject *)_new_bound_callable_info (self, bound_arg); + Py_DECREF (bound_arg); + return result; +} + +static void +_callable_info_dealloc (PyGICallableInfo *self) +{ + Py_CLEAR (self->py_unbound_info); + Py_CLEAR (self->py_bound_arg); + + PyGIBaseInfo_Type.tp_dealloc ((PyObject *) self); +} + static PyMethodDef _PyGICallableInfo_methods[] = { { "invoke", (PyCFunction) _wrap_g_callable_info_invoke, METH_VARARGS | METH_KEYWORDS }, { "get_arguments", (PyCFunction) _wrap_g_callable_info_get_arguments, METH_NOARGS }, @@ -427,6 +680,7 @@ _g_arg_get_pytype_hint (PyGIBaseInfo *self) const char *info_name; PyObject *py_string; GIBaseInfo *iface = g_type_info_get_interface(&type_info); + gchar *name; info_name = g_base_info_get_name (iface); if (info_name == NULL) { @@ -434,7 +688,7 @@ _g_arg_get_pytype_hint (PyGIBaseInfo *self) return PYGLIB_PyUnicode_FromString(g_type_tag_to_string(type_tag)); } - gchar *name = g_strdup_printf("%s.%s", + name = g_strdup_printf("%s.%s", g_base_info_get_namespace(iface), info_name); g_base_info_unref(iface); @@ -466,7 +720,7 @@ static PyMethodDef _PyGITypeInfo_methods[] = { /* FunctionInfo */ -PYGLIB_DEFINE_TYPE ("gi.FunctionInfo", PyGIFunctionInfo_Type, PyGIBaseInfo); +PYGLIB_DEFINE_TYPE ("gi.FunctionInfo", PyGIFunctionInfo_Type, PyGICallableInfo); static PyObject * _wrap_g_function_info_is_constructor (PyGIBaseInfo *self) @@ -652,7 +906,6 @@ static PyMethodDef _PyGIFunctionInfo_methods[] = { { NULL, NULL, 0 } }; - /* RegisteredTypeInfo */ PYGLIB_DEFINE_TYPE ("gi.RegisteredTypeInfo", PyGIRegisteredTypeInfo_Type, PyGIBaseInfo); @@ -1590,7 +1843,7 @@ static PyMethodDef _PyGIUnresolvedInfo_methods[] = { }; /* GIVFuncInfo */ -PYGLIB_DEFINE_TYPE ("gi.VFuncInfo", PyGIVFuncInfo_Type, PyGIBaseInfo); +PYGLIB_DEFINE_TYPE ("gi.VFuncInfo", PyGIVFuncInfo_Type, PyGICallableInfo); static PyObject * _wrap_g_vfunc_info_get_invoker (PyGIBaseInfo *self) @@ -1741,12 +1994,13 @@ _pygi_info_register_types (PyObject *m) PyGIBaseInfo_Type.tp_repr = (reprfunc) _base_info_repr; PyGIBaseInfo_Type.tp_flags = (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE); PyGIBaseInfo_Type.tp_weaklistoffset = offsetof(PyGIBaseInfo, inst_weakreflist); - PyGIBaseInfo_Type.tp_methods = _PyGIBaseInfo_methods; + PyGIBaseInfo_Type.tp_methods = _PyGIBaseInfo_methods; PyGIBaseInfo_Type.tp_richcompare = (richcmpfunc)_base_info_richcompare; + PyGIBaseInfo_Type.tp_getset = _base_info_getsets; + PyGIBaseInfo_Type.tp_getattro = (getattrofunc) _base_info_getattro; if (PyType_Ready(&PyGIBaseInfo_Type)) - return; - + return; if (PyModule_AddObject(m, "BaseInfo", (PyObject *)&PyGIBaseInfo_Type)) return; @@ -1757,14 +2011,24 @@ _pygi_info_register_types (PyObject *m) 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, PyGIBaseInfo_Type); - _PyGI_REGISTER_TYPE (m, PyGICallbackInfo_Type, CallbackInfo, - PyGIBaseInfo_Type); + PyGICallableInfo_Type.tp_call = (ternaryfunc) _callable_info_call; + PyGICallableInfo_Type.tp_dealloc = (destructor) _callable_info_dealloc; + _PyGI_REGISTER_TYPE (m, PyGIFunctionInfo_Type, FunctionInfo, PyGICallableInfo_Type); + PyGIFunctionInfo_Type.tp_call = (ternaryfunc) _function_info_call; + PyGIFunctionInfo_Type.tp_descr_get = (descrgetfunc) _function_info_descr_get; + + _PyGI_REGISTER_TYPE (m, PyGIVFuncInfo_Type, VFuncInfo, + PyGICallableInfo_Type); + PyGIVFuncInfo_Type.tp_descr_get = (descrgetfunc) _vfunc_info_descr_get; + + _PyGI_REGISTER_TYPE (m, PyGIUnresolvedInfo_Type, UnresolvedInfo, + PyGIBaseInfo_Type); + _PyGI_REGISTER_TYPE (m, PyGICallbackInfo_Type, CallbackInfo, + PyGIBaseInfo_Type); _PyGI_REGISTER_TYPE (m, PyGIRegisteredTypeInfo_Type, RegisteredTypeInfo, PyGIBaseInfo_Type); _PyGI_REGISTER_TYPE (m, PyGIStructInfo_Type, StructInfo, @@ -1781,8 +2045,6 @@ _pygi_info_register_types (PyObject *m) PyGIBaseInfo_Type); _PyGI_REGISTER_TYPE (m, PyGIFieldInfo_Type, FieldInfo, PyGIBaseInfo_Type); - _PyGI_REGISTER_TYPE (m, PyGIVFuncInfo_Type, VFuncInfo, - PyGICallableInfo_Type); _PyGI_REGISTER_TYPE (m, PyGIUnionInfo_Type, UnionInfo, PyGIRegisteredTypeInfo_Type); _PyGI_REGISTER_TYPE (m, PyGIBoxedInfo_Type, BoxedInfo, |