From f5dd1551371308d33df9627bc8cc0e34fa68a2a8 Mon Sep 17 00:00:00 2001 From: Christoph Reiter Date: Tue, 31 May 2016 18:47:54 +0200 Subject: Remove pygobject-private.h and rename pygobject.c to pygobject-object.c Move all the random declarations in pygobject-private.h to their respective header files. Rename pygobject.c to pygobject-object.c so it's clearer that it's not the implementation of pygobject.h. Add a new pygobject-internal.h which includes pygobject.h with _INSIDE_PYGOBJECT_ defined like pygobject-private.h did. In case you are looking at the git log and end up here due to the rename try: git log --follow pygobject-object.c or on the web interface go to the history of the old file name: https://git.gnome.org/browse/pygobject/log/gi/pygobject.c?id=6b702c052e9f26e809cff494f0c896d17a514c64 https://bugzilla.gnome.org/show_bug.cgi?id=767084 --- gi/Makefile.am | 7 +- gi/gimodule.c | 10 +- gi/gobjectmodule.c | 56 +- gi/gobjectmodule.h | 11 + gi/pygboxed.c | 4 +- gi/pygboxed.h | 11 + gi/pygenum.c | 4 +- gi/pygenum.h | 22 + gi/pygflags.c | 4 +- gi/pygflags.h | 21 + gi/pygi-argument.c | 4 +- gi/pygi-basictype.c | 2 +- gi/pygi-boxed.c | 3 +- gi/pygi-boxed.h | 2 +- gi/pygi-cache.c | 1 + gi/pygi-ccallback.c | 1 - gi/pygi-enum-marshal.c | 3 +- gi/pygi-foreign.c | 2 +- gi/pygi-info.c | 2 +- gi/pygi-object.c | 2 +- gi/pygi-property.c | 1 + gi/pygi-property.h | 2 +- gi/pygi-signal-closure.h | 2 +- gi/pygi-source.c | 3 +- gi/pygi-struct-marshal.c | 3 + gi/pygi-struct.c | 3 +- gi/pygi-struct.h | 2 +- gi/pygi-type.c | 2 +- gi/pygi-util.c | 42 + gi/pygi-util.h | 6 + gi/pygi-value.c | 6 +- gi/pyginterface.c | 3 +- gi/pygobject-internal.h | 7 + gi/pygobject-object.c | 2429 ++++++++++++++++++++++++++++++++++++++++++++++ gi/pygobject-object.h | 56 ++ gi/pygobject-private.h | 186 ---- gi/pygobject.c | 2406 --------------------------------------------- gi/pygparamspec.c | 5 +- gi/pygpointer.c | 3 +- gi/pygpointer.h | 8 + gi/pygtype.c | 27 +- gi/pygtype.h | 25 + 42 files changed, 2709 insertions(+), 2690 deletions(-) create mode 100644 gi/gobjectmodule.h create mode 100644 gi/pygi-util.c create mode 100644 gi/pygobject-internal.h create mode 100644 gi/pygobject-object.c create mode 100644 gi/pygobject-object.h delete mode 100644 gi/pygobject-private.h delete mode 100644 gi/pygobject.c diff --git a/gi/Makefile.am b/gi/Makefile.am index 3dee5042..defe77e9 100644 --- a/gi/Makefile.am +++ b/gi/Makefile.am @@ -36,6 +36,7 @@ pygi_LTLIBRARIES = _gi.la _gi_la_SOURCES = \ gobjectmodule.c \ + gobjectmodule.h \ pygboxed.c \ pygboxed.h \ pygenum.c \ @@ -44,9 +45,10 @@ _gi_la_SOURCES = \ pygflags.h \ pyginterface.c \ pyginterface.h \ - pygobject.c \ pygobject.h \ - pygobject-private.h \ + pygobject-internal.h \ + pygobject-object.c \ + pygobject-object.h \ pygparamspec.c \ pygparamspec.h \ pygpointer.c \ @@ -87,6 +89,7 @@ _gi_la_SOURCES = \ pygi-closure.h \ pygi-ccallback.c \ pygi-ccallback.h \ + pygi-util.c \ pygi-util.h \ pygi-property.c \ pygi-property.h \ diff --git a/gi/gimodule.c b/gi/gimodule.c index 9206a508..d60a6a56 100644 --- a/gi/gimodule.c +++ b/gi/gimodule.c @@ -21,11 +21,16 @@ * USA */ +#include + #include "pyglib.h" -#include "pygobject-private.h" #include "pyginterface.h" #include "pygi-repository.h" #include "pyglib.h" +#include "pygtype.h" +#include "pygenum.h" +#include "pygboxed.h" +#include "pygflags.h" #include "pygi-error.h" #include "pygi-foreign.h" #include "pygi-resulttuple.h" @@ -44,6 +49,9 @@ PyObject *PyGIDeprecationWarning; PyObject *_PyGIDefaultArgPlaceholder; +/* Defined by PYGLIB_MODULE_START */ +extern PyObject *pyglib__gobject_module_create (void); + /* Returns a new flag/enum type or %NULL */ static PyObject * flags_enum_from_gtype (GType g_type, diff --git a/gi/gobjectmodule.c b/gi/gobjectmodule.c index 726216dd..0dc26707 100644 --- a/gi/gobjectmodule.c +++ b/gi/gobjectmodule.c @@ -27,7 +27,7 @@ #include #include #include -#include "pygobject-private.h" +#include "gobjectmodule.h" #include "pygboxed.h" #include "pygenum.h" #include "pygflags.h" @@ -36,6 +36,7 @@ #include "pygpointer.h" #include "pygtype.h" #include "pygoptiongroup.h" +#include "pygobject-object.h" #include "pygi-value.h" #include "pygi-error.h" @@ -47,6 +48,7 @@ static gboolean log_handlers_disabled = FALSE; static void pyg_flags_add_constants(PyObject *module, GType flags_type, const gchar *strip_prefix); +static int pyg_type_register(PyTypeObject *class, const char *type_name); /* -------------- GDK threading hooks ---------------------------- */ @@ -72,7 +74,7 @@ _pyg_set_thread_block_funcs (PyGThreadBlockFunc block_threads_func, * A function that can be used as a GDestroyNotify callback that will * call Py_DECREF on the data. */ -void +static void pyg_destroy_notify(gpointer user_data) { PyObject *obj = (PyObject *)user_data; @@ -1098,7 +1100,7 @@ pyg_type_add_interfaces(PyTypeObject *class, GType instance_type, } } -int +static int pyg_type_register(PyTypeObject *class, const char *type_name) { PyObject *gtype; @@ -1390,33 +1392,6 @@ pyg_object_new (PyGObject *self, PyObject *args, PyObject *kwargs) return (PyObject *) self; } -gboolean -pyg_handler_marshal(gpointer user_data) -{ - PyObject *tuple, *ret; - gboolean res; - PyGILState_STATE state; - - g_return_val_if_fail(user_data != NULL, FALSE); - - state = pyglib_gil_state_ensure(); - - tuple = (PyObject *)user_data; - ret = PyObject_CallObject(PyTuple_GetItem(tuple, 0), - PyTuple_GetItem(tuple, 1)); - if (!ret) { - PyErr_Print(); - res = FALSE; - } else { - res = PyObject_IsTrue(ret); - Py_DECREF(ret); - } - - pyglib_gil_state_release(state); - - return res; -} - static int pygobject_gil_state_ensure (void) { @@ -1797,27 +1772,6 @@ pyg_parse_constructor_args(GType obj_type, return TRUE; } -PyObject * -pyg_integer_richcompare(PyObject *v, PyObject *w, int op) -{ - PyObject *result; - gboolean t; - - switch (op) { - case Py_EQ: t = PYGLIB_PyLong_AS_LONG(v) == PYGLIB_PyLong_AS_LONG(w); break; - case Py_NE: t = PYGLIB_PyLong_AS_LONG(v) != PYGLIB_PyLong_AS_LONG(w); break; - case Py_LE: t = PYGLIB_PyLong_AS_LONG(v) <= PYGLIB_PyLong_AS_LONG(w); break; - case Py_GE: t = PYGLIB_PyLong_AS_LONG(v) >= PYGLIB_PyLong_AS_LONG(w); break; - case Py_LT: t = PYGLIB_PyLong_AS_LONG(v) < PYGLIB_PyLong_AS_LONG(w); break; - case Py_GT: t = PYGLIB_PyLong_AS_LONG(v) > PYGLIB_PyLong_AS_LONG(w); break; - default: g_assert_not_reached(); - } - - result = t ? Py_True : Py_False; - Py_INCREF(result); - return result; -} - static void _log_func(const gchar *log_domain, GLogLevelFlags log_level, diff --git a/gi/gobjectmodule.h b/gi/gobjectmodule.h new file mode 100644 index 00000000..11f99cf3 --- /dev/null +++ b/gi/gobjectmodule.h @@ -0,0 +1,11 @@ +#ifndef _PYGOBJECT_GOBJECTMODULE_H_ +#define _PYGOBJECT_GOBJECTMODULE_H_ + + +#include "pygobject-internal.h" + +int pygobject_constructv (PyGObject *self, + guint n_parameters, + GParameter *parameters); + +#endif /*_PYGOBJECT_GOBJECTMODULE_H_*/ diff --git a/gi/pygboxed.c b/gi/pygboxed.c index 814cdb95..2eaff851 100644 --- a/gi/pygboxed.c +++ b/gi/pygboxed.c @@ -22,9 +22,11 @@ # include #endif +#include + #include -#include "pygobject-private.h" #include "pygboxed.h" +#include "pygtype.h" #include "pygi-type.h" diff --git a/gi/pygboxed.h b/gi/pygboxed.h index 86f72d59..93b3de65 100644 --- a/gi/pygboxed.h +++ b/gi/pygboxed.h @@ -20,6 +20,17 @@ #ifndef __PYGOBJECT_BOXED_H__ #define __PYGOBJECT_BOXED_H__ +extern GQuark pygboxed_type_key; + +extern PyTypeObject PyGBoxed_Type; + +void pyg_register_boxed (PyObject *dict, const gchar *class_name, + GType boxed_type, PyTypeObject *type); +PyObject * pyg_boxed_new (GType boxed_type, gpointer boxed, + gboolean copy_boxed, gboolean own_ref); + +const gchar * pyg_constant_strip_prefix(const gchar *name, const gchar *strip_prefix); + void pygobject_boxed_register_types(PyObject *d); #endif /* __PYGOBJECT_BOXED_H__ */ diff --git a/gi/pygenum.c b/gi/pygenum.c index fb0873de..cea9c0fe 100644 --- a/gi/pygenum.c +++ b/gi/pygenum.c @@ -25,10 +25,12 @@ #include #include "pyglib-python-compat.h" -#include "pygobject-private.h" #include "pygi-type.h" +#include "pygi-util.h" +#include "pygtype.h" #include "pygenum.h" +#include "pygboxed.h" GQuark pygenum_class_key; diff --git a/gi/pygenum.h b/gi/pygenum.h index 6c01ec97..0625a949 100644 --- a/gi/pygenum.h +++ b/gi/pygenum.h @@ -20,6 +20,28 @@ #ifndef __PYGOBJECT_ENUM_H__ #define __PYGOBJECT_ENUM_H__ +extern GQuark pygenum_class_key; + +#define PyGEnum_Check(x) (PyObject_IsInstance((PyObject *)x, (PyObject *)&PyGEnum_Type) && g_type_is_a(((PyGFlags*)x)->gtype, G_TYPE_ENUM)) + +typedef struct { + PYGLIB_PyLongObject parent; + int zero_pad; /* must always be 0 */ + GType gtype; +} PyGEnum; + +extern PyTypeObject PyGEnum_Type; + +PyObject * pyg_enum_add (PyObject * module, + const char * type_name, + const char * strip_prefix, + GType gtype); + +PyObject * pyg_enum_from_gtype (GType gtype, + int value); + +gint pyg_enum_get_value (GType enum_type, PyObject *obj, gint *val); + void pygobject_enum_register_types(PyObject *d); #endif /* __PYGOBJECT_ENUM_H__ */ diff --git a/gi/pygflags.c b/gi/pygflags.c index 0be3097e..693fca07 100644 --- a/gi/pygflags.c +++ b/gi/pygflags.c @@ -25,8 +25,10 @@ #include #include "pygi-type.h" -#include "pygobject-private.h" +#include "pygi-util.h" +#include "pygtype.h" #include "pygflags.h" +#include "pygboxed.h" GQuark pygflags_class_key; diff --git a/gi/pygflags.h b/gi/pygflags.h index 3c7b0e82..9555b945 100644 --- a/gi/pygflags.h +++ b/gi/pygflags.h @@ -20,6 +20,27 @@ #ifndef __PYGOBJECT_FLAGS_H__ #define __PYGOBJECT_FLAGS_H__ +extern GQuark pygflags_class_key; + +typedef struct { + PYGLIB_PyLongObject parent; + int zero_pad; /* must always be 0 */ + GType gtype; +} PyGFlags; + +extern PyTypeObject PyGFlags_Type; + +#define PyGFlags_Check(x) (PyObject_IsInstance((PyObject *)x, (PyObject *)&PyGFlags_Type) && g_type_is_a(((PyGFlags*)x)->gtype, G_TYPE_FLAGS)) + +extern PyObject * pyg_flags_add (PyObject * module, + const char * type_name, + const char * strip_prefix, + GType gtype); +extern PyObject * pyg_flags_from_gtype (GType gtype, + guint value); + +gint pyg_flags_get_value (GType flag_type, PyObject *obj, guint *val); + void pygobject_flags_register_types(PyObject *d); #endif /* __PYGOBJECT_FLAGS_H__ */ diff --git a/gi/pygi-argument.c b/gi/pygi-argument.c index 2e4dd400..e9bfe3b3 100644 --- a/gi/pygi-argument.c +++ b/gi/pygi-argument.c @@ -22,10 +22,12 @@ #include #include -#include "pygobject-private.h" +#include "pygobject-internal.h" #include #include +#include +#include #include "pygi-argument.h" #include "pygi-info.h" diff --git a/gi/pygi-basictype.c b/gi/pygi-basictype.c index 470547d4..b6515c31 100644 --- a/gi/pygi-basictype.c +++ b/gi/pygi-basictype.c @@ -21,9 +21,9 @@ #include #include +#include "pygtype.h" #include "pygi-basictype.h" #include "pygi-argument.h" -#include "pygobject-private.h" #ifdef G_OS_WIN32 #include diff --git a/gi/pygi-boxed.c b/gi/pygi-boxed.c index c1e4b0ea..e9014f20 100644 --- a/gi/pygi-boxed.c +++ b/gi/pygi-boxed.c @@ -21,7 +21,8 @@ #include "pygi-boxed.h" #include "pygi-info.h" -#include "pygobject-private.h" +#include "pygboxed.h" +#include "pygtype.h" #include #include diff --git a/gi/pygi-boxed.h b/gi/pygi-boxed.h index 89942d07..86793224 100644 --- a/gi/pygi-boxed.h +++ b/gi/pygi-boxed.h @@ -22,7 +22,7 @@ #include #include -#include "pygobject-private.h" +#include "pygobject-internal.h" G_BEGIN_DECLS diff --git a/gi/pygi-cache.c b/gi/pygi-cache.c index 263dc8ef..5080b666 100644 --- a/gi/pygi-cache.c +++ b/gi/pygi-cache.c @@ -22,6 +22,7 @@ #include #include "pyglib.h" +#include "pygtype.h" #include "pygi-info.h" #include "pygi-cache.h" #include "pygi-marshal-cleanup.h" diff --git a/gi/pygi-ccallback.c b/gi/pygi-ccallback.c index c7f3ecfe..3fe5366a 100644 --- a/gi/pygi-ccallback.c +++ b/gi/pygi-ccallback.c @@ -19,7 +19,6 @@ * License along with this library; if not, see . */ -#include "pygobject-private.h" #include "pygi-ccallback.h" #include diff --git a/gi/pygi-enum-marshal.c b/gi/pygi-enum-marshal.c index 945a8e0b..11c20499 100644 --- a/gi/pygi-enum-marshal.c +++ b/gi/pygi-enum-marshal.c @@ -23,8 +23,9 @@ #include #include "pygi-enum-marshal.h" -#include "pygobject-private.h" #include "pygi-type.h" +#include "pygenum.h" +#include "pygflags.h" static gboolean gi_argument_from_c_long (GIArgument *arg_out, diff --git a/gi/pygi-foreign.c b/gi/pygi-foreign.c index 82392be1..f74b1e7c 100644 --- a/gi/pygi-foreign.c +++ b/gi/pygi-foreign.c @@ -26,7 +26,7 @@ # include #endif -#include "pygobject-private.h" +#include "pygobject-internal.h" #include "pygi-foreign.h" #include diff --git a/gi/pygi-info.c b/gi/pygi-info.c index 3422ea96..09c513b3 100644 --- a/gi/pygi-info.c +++ b/gi/pygi-info.c @@ -26,7 +26,7 @@ #include "pygi-type.h" #include "pygi-argument.h" #include "pygi-util.h" -#include "pygobject-private.h" +#include "pygtype.h" #include diff --git a/gi/pygi-object.c b/gi/pygi-object.c index 11ea2261..35a27908 100644 --- a/gi/pygi-object.c +++ b/gi/pygi-object.c @@ -23,7 +23,7 @@ #include #include "pygi-object.h" -#include "pygobject-private.h" +#include "pygobject-object.h" #include "pygparamspec.h" /* diff --git a/gi/pygi-property.c b/gi/pygi-property.c index 4eb9ca8a..19cdb440 100644 --- a/gi/pygi-property.c +++ b/gi/pygi-property.c @@ -25,6 +25,7 @@ #include "pygi-value.h" #include "pygi-argument.h" #include "pygparamspec.h" +#include "pygtype.h" #include diff --git a/gi/pygi-property.h b/gi/pygi-property.h index 19c720fb..d641b018 100644 --- a/gi/pygi-property.h +++ b/gi/pygi-property.h @@ -27,7 +27,7 @@ #include #include -#include "pygobject-private.h" +#include "pygobject-internal.h" PyObject * pygi_get_property_value (PyGObject *instance, diff --git a/gi/pygi-signal-closure.h b/gi/pygi-signal-closure.h index 80bc58c5..92e18708 100644 --- a/gi/pygi-signal-closure.h +++ b/gi/pygi-signal-closure.h @@ -25,7 +25,7 @@ #define __PYGI_SIGNAL_CLOSURE_H__ #include -#include "pygobject-private.h" +#include "pygobject-internal.h" G_BEGIN_DECLS diff --git a/gi/pygi-source.c b/gi/pygi-source.c index 1a847887..154b1cd6 100644 --- a/gi/pygi-source.c +++ b/gi/pygi-source.c @@ -23,12 +23,11 @@ * IN THE SOFTWARE. */ -#include "pygobject-private.h" - #include "pygi-info.h" #include "pygi-boxed.h" #include "pygi-type.h" #include "pyglib.h" +#include "pygboxed.h" #include "pygi-source.h" typedef struct diff --git a/gi/pygi-struct-marshal.c b/gi/pygi-struct-marshal.c index 0d365217..a4276a47 100644 --- a/gi/pygi-struct-marshal.c +++ b/gi/pygi-struct-marshal.c @@ -29,6 +29,9 @@ #include "pygi-type.h" #include "pygi-boxed.h" #include "pygi-info.h" +#include "pygpointer.h" +#include "pygboxed.h" +#include "pygtype.h" /* * _is_union_member - check to see if the py_arg is actually a member of the diff --git a/gi/pygi-struct.c b/gi/pygi-struct.c index 5bbb7899..4d5b5411 100644 --- a/gi/pygi-struct.c +++ b/gi/pygi-struct.c @@ -23,7 +23,8 @@ #include "pygi-foreign.h" #include "pygi-info.h" #include "pygi-type.h" -#include "pygobject-private.h" +#include "pygtype.h" +#include "pygpointer.h" #include #include diff --git a/gi/pygi-struct.h b/gi/pygi-struct.h index dde42dd4..8796a5e4 100644 --- a/gi/pygi-struct.h +++ b/gi/pygi-struct.h @@ -21,7 +21,7 @@ #define __PYGI_STRUCT_H__ #include -#include +#include G_BEGIN_DECLS diff --git a/gi/pygi-type.c b/gi/pygi-type.c index a1213f71..06d773a3 100644 --- a/gi/pygi-type.c +++ b/gi/pygi-type.c @@ -19,7 +19,7 @@ * License along with this library; if not, see . */ -#include "pygobject-private.h" +#include "pygtype.h" #include "pygi-type.h" #include diff --git a/gi/pygi-util.c b/gi/pygi-util.c new file mode 100644 index 00000000..1d9201e6 --- /dev/null +++ b/gi/pygi-util.c @@ -0,0 +1,42 @@ +/* -*- Mode: C; c-basic-offset: 4 -*- + * pygtk- Python bindings for the GTK toolkit. + * Copyright (C) 1998-2003 James Henstridge + * + * gobjectmodule.c: wrapper for the gobject library. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see . + */ + +#include "pygi-util.h" + +PyObject * +pyg_integer_richcompare(PyObject *v, PyObject *w, int op) +{ + PyObject *result; + gboolean t; + + switch (op) { + case Py_EQ: t = PYGLIB_PyLong_AS_LONG(v) == PYGLIB_PyLong_AS_LONG(w); break; + case Py_NE: t = PYGLIB_PyLong_AS_LONG(v) != PYGLIB_PyLong_AS_LONG(w); break; + case Py_LE: t = PYGLIB_PyLong_AS_LONG(v) <= PYGLIB_PyLong_AS_LONG(w); break; + case Py_GE: t = PYGLIB_PyLong_AS_LONG(v) >= PYGLIB_PyLong_AS_LONG(w); break; + case Py_LT: t = PYGLIB_PyLong_AS_LONG(v) < PYGLIB_PyLong_AS_LONG(w); break; + case Py_GT: t = PYGLIB_PyLong_AS_LONG(v) > PYGLIB_PyLong_AS_LONG(w); break; + default: g_assert_not_reached(); + } + + result = t ? Py_True : Py_False; + Py_INCREF(result); + return result; +} diff --git a/gi/pygi-util.h b/gi/pygi-util.h index 3144d6e4..c7a6ca7b 100644 --- a/gi/pygi-util.h +++ b/gi/pygi-util.h @@ -1,8 +1,14 @@ #ifndef __PYGI_UTIL_H__ #define __PYGI_UTIL_H__ +#include +#include "pygobject-internal.h" +#include + G_BEGIN_DECLS +PyObject * pyg_integer_richcompare(PyObject *v, PyObject *w, int op); + #if PY_VERSION_HEX >= 0x03000000 #define _PyGI_ERROR_PREFIX(format, ...) G_STMT_START { \ diff --git a/gi/pygi-value.c b/gi/pygi-value.c index 9da87a56..88faf630 100644 --- a/gi/pygi-value.c +++ b/gi/pygi-value.c @@ -20,8 +20,12 @@ #include "pygi-value.h" #include "pygi-struct.h" #include "pyglib-python-compat.h" -#include "pygobject-private.h" +#include "pygobject-object.h" #include "pygtype.h" +#include "pygenum.h" +#include "pygpointer.h" +#include "pygboxed.h" +#include "pygflags.h" #include "pygparamspec.h" GIArgument diff --git a/gi/pyginterface.c b/gi/pyginterface.c index 40d54f7f..1737de5a 100644 --- a/gi/pyginterface.c +++ b/gi/pyginterface.c @@ -23,10 +23,11 @@ #endif #include +#include #include "pyglib.h" -#include "pygobject-private.h" #include "pyginterface.h" +#include "pygtype.h" GQuark pyginterface_type_key; GQuark pyginterface_info_key; diff --git a/gi/pygobject-internal.h b/gi/pygobject-internal.h new file mode 100644 index 00000000..2cd82c53 --- /dev/null +++ b/gi/pygobject-internal.h @@ -0,0 +1,7 @@ +#ifndef _PYGOBJECT_INTERNAL_H_ +#define _PYGOBJECT_INTERNAL_H_ + +#define _INSIDE_PYGOBJECT_ +#include "pygobject.h" + +#endif /*_PYGOBJECT_INTERNAL_H_*/ diff --git a/gi/pygobject-object.c b/gi/pygobject-object.c new file mode 100644 index 00000000..b2fe4712 --- /dev/null +++ b/gi/pygobject-object.c @@ -0,0 +1,2429 @@ +/* -*- Mode: C; c-basic-offset: 4 -*- + * pygtk- Python bindings for the GTK toolkit. + * Copyright (C) 1998-2003 James Henstridge + * + * pygobject.c: wrapper for the GObject type. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see . + */ + +#ifdef HAVE_CONFIG_H +# include +#endif + +#include +#include "pygobject-object.h" +#include "pyginterface.h" +#include "pygparamspec.h" +#include "pygtype.h" +#include "pygboxed.h" +#include "gobjectmodule.h" + +#include "pygi-value.h" +#include "pygi-type.h" +#include "pygi-property.h" +#include "pygi-signal-closure.h" + +extern PyObject *PyGIDeprecationWarning; + +static void pygobject_dealloc(PyGObject *self); +static int pygobject_traverse(PyGObject *self, visitproc visit, void *arg); +static int pygobject_clear(PyGObject *self); +static PyObject * pyg_type_get_bases(GType gtype); +static inline int pygobject_clear(PyGObject *self); +static PyObject * pygobject_weak_ref_new(GObject *obj, PyObject *callback, PyObject *user_data); +static inline PyGObjectData * pyg_object_peek_inst_data(GObject *obj); +static void pygobject_inherit_slots(PyTypeObject *type, PyObject *bases, + gboolean check_for_present); +static void pygobject_find_slot_for(PyTypeObject *type, PyObject *bases, int slot_offset, + gboolean check_for_present); +GType PY_TYPE_OBJECT = 0; +GQuark pygobject_custom_key; +GQuark pygobject_class_key; +GQuark pygobject_class_init_key; +GQuark pygobject_wrapper_key; +GQuark pygobject_has_updated_constructor_key; +GQuark pygobject_instance_data_key; + +GClosure * +gclosure_from_pyfunc(PyGObject *object, PyObject *func) +{ + GSList *l; + PyGObjectData *inst_data; + inst_data = pyg_object_peek_inst_data(object->obj); + if (inst_data) { + for (l = inst_data->closures; l; l = l->next) { + PyGClosure *pyclosure = l->data; + int res = PyObject_RichCompareBool(pyclosure->callback, func, Py_EQ); + if (res == -1) { + PyErr_Clear(); /* Is there anything else to do? */ + } else if (res) { + return (GClosure*)pyclosure; + } + } + } + return NULL; +} + +/* Copied from glib. gobject uses hyphens in property names, but in Python + * we can only represent hyphens as underscores. Convert underscores to + * hyphens for glib compatibility. */ +static void +canonicalize_key (gchar *key) +{ + gchar *p; + + for (p = key; *p != 0; p++) + { + gchar c = *p; + + if (c != '-' && + (c < '0' || c > '9') && + (c < 'A' || c > 'Z') && + (c < 'a' || c > 'z')) + *p = '-'; + } +} + +/* -------------- class <-> wrapper manipulation --------------- */ + +static void +pygobject_data_free(PyGObjectData *data) +{ + /* This function may be called after the python interpreter has already + * been shut down. If this happens, we cannot do any python calls, so just + * free the memory. */ + PyGILState_STATE state; + PyThreadState *_save = NULL; + gboolean state_saved = FALSE; + + GSList *closures, *tmp; + + if (Py_IsInitialized()) { + state_saved = TRUE; + state = pyglib_gil_state_ensure(); + Py_DECREF(data->type); + /* We cannot use Py_BEGIN_ALLOW_THREADS here because this is inside + * a branch. */ + Py_UNBLOCK_THREADS; /* Modifies _save */ + } + + tmp = closures = data->closures; +#ifndef NDEBUG + data->closures = NULL; + data->type = NULL; +#endif + while (tmp) { + GClosure *closure = tmp->data; + + /* we get next item first, because the current link gets + * invalidated by pygobject_unwatch_closure */ + tmp = tmp->next; + g_closure_invalidate(closure); + } + + if (data->closures != NULL) + g_warning("invalidated all closures, but data->closures != NULL !"); + + g_free(data); + + if (state_saved && Py_IsInitialized ()) { + Py_BLOCK_THREADS; /* Restores _save */ + pyglib_gil_state_release(state); + } +} + +static inline PyGObjectData * +pygobject_data_new(void) +{ + PyGObjectData *data; + data = g_new0(PyGObjectData, 1); + return data; +} + +static inline PyGObjectData * +pygobject_get_inst_data(PyGObject *self) +{ + PyGObjectData *inst_data; + + if (G_UNLIKELY(!self->obj)) + return NULL; + inst_data = g_object_get_qdata(self->obj, pygobject_instance_data_key); + if (inst_data == NULL) + { + inst_data = pygobject_data_new(); + + inst_data->type = Py_TYPE(self); + Py_INCREF((PyObject *) inst_data->type); + + g_object_set_qdata_full(self->obj, pygobject_instance_data_key, + inst_data, (GDestroyNotify) pygobject_data_free); + } + return inst_data; +} + + +PyTypeObject *PyGObject_MetaType = NULL; + +/** + * pygobject_sink: + * @obj: a GObject + * + * As Python handles reference counting for us, the "floating + * reference" code in GTK is not all that useful. In fact, it can + * cause leaks. This function should be called to remove the floating + * references on objects on construction. + **/ +void +pygobject_sink(GObject *obj) +{ + /* The default behaviour for GInitiallyUnowned subclasses is to call ref_sink(). + * - if the object is new and owned by someone else, its ref has been sunk and + * we need to keep the one from that someone and add our own "fresh ref" + * - if the object is not and owned by nobody, its ref is floating and we need + * to transform it into a regular ref. + */ + if (G_IS_INITIALLY_UNOWNED(obj)) { + g_object_ref_sink(obj); + } +} + +typedef struct { + PyObject_HEAD + GParamSpec **props; + guint n_props; + guint index; +} PyGPropsIter; + +PYGLIB_DEFINE_TYPE("gi._gobject.GPropsIter", PyGPropsIter_Type, PyGPropsIter); + +static void +pyg_props_iter_dealloc(PyGPropsIter *self) +{ + g_free(self->props); + PyObject_Del((PyObject*) self); +} + +static PyObject* +pygobject_props_iter_next(PyGPropsIter *iter) +{ + if (iter->index < iter->n_props) + return pyg_param_spec_new(iter->props[iter->index++]); + else { + PyErr_SetNone(PyExc_StopIteration); + return NULL; + } +} + +typedef struct { + PyObject_HEAD + /* a reference to the object containing the properties */ + PyGObject *pygobject; + GType gtype; +} PyGProps; + +static void +PyGProps_dealloc(PyGProps* self) +{ + PyGObject *tmp; + + PyObject_GC_UnTrack((PyObject*)self); + + tmp = self->pygobject; + self->pygobject = NULL; + Py_XDECREF(tmp); + + PyObject_GC_Del((PyObject*)self); +} + +static PyObject* +build_parameter_list(GObjectClass *class) +{ + GParamSpec **props; + guint n_props = 0, i; + PyObject *prop_str; + PyObject *props_list; + + props = g_object_class_list_properties(class, &n_props); + props_list = PyList_New(n_props); + for (i = 0; i < n_props; i++) { + char *name; + name = g_strdup(g_param_spec_get_name(props[i])); + /* hyphens cannot belong in identifiers */ + g_strdelimit(name, "-", '_'); + prop_str = PYGLIB_PyUnicode_FromString(name); + + PyList_SetItem(props_list, i, prop_str); + g_free(name); + } + + if (props) + g_free(props); + + return props_list; +} + +static PyObject* +PyGProps_getattro(PyGProps *self, PyObject *attr) +{ + char *attr_name, *property_name; + GObjectClass *class; + GParamSpec *pspec; + + attr_name = PYGLIB_PyUnicode_AsString(attr); + if (!attr_name) { + PyErr_Clear(); + return PyObject_GenericGetAttr((PyObject *)self, attr); + } + + class = g_type_class_ref(self->gtype); + + /* g_object_class_find_property recurses through the class hierarchy, + * so the resulting pspec tells us the owner_type that owns the property + * we're dealing with. */ + property_name = g_strdup(attr_name); + canonicalize_key(property_name); + pspec = g_object_class_find_property(class, property_name); + g_free(property_name); + g_type_class_unref(class); + + if (!pspec) { + return PyObject_GenericGetAttr((PyObject *)self, attr); + } + + if (!self->pygobject) { + /* If we're doing it without an instance, return a GParamSpec */ + return pyg_param_spec_new(pspec); + } + + return pygi_get_property_value (self->pygobject, pspec); +} + +static gboolean +set_property_from_pspec(GObject *obj, + GParamSpec *pspec, + PyObject *pvalue) +{ + GValue value = { 0, }; + + if (pspec->flags & G_PARAM_CONSTRUCT_ONLY) { + PyErr_Format(PyExc_TypeError, + "property '%s' can only be set in constructor", + pspec->name); + return FALSE; + } + + if (!(pspec->flags & G_PARAM_WRITABLE)) { + PyErr_Format(PyExc_TypeError, + "property '%s' is not writable", pspec->name); + return FALSE; + } + + g_value_init(&value, G_PARAM_SPEC_VALUE_TYPE(pspec)); + if (pyg_param_gvalue_from_pyobject(&value, pvalue, pspec) < 0) { + PyObject *pvalue_str = PyObject_Str(pvalue); + PyErr_Format(PyExc_TypeError, + "could not convert '%s' to type '%s' when setting property '%s.%s'", + PYGLIB_PyUnicode_AsString(pvalue_str), + g_type_name(G_PARAM_SPEC_VALUE_TYPE(pspec)), + G_OBJECT_TYPE_NAME(obj), + pspec->name); + Py_DECREF(pvalue_str); + return FALSE; + } + + Py_BEGIN_ALLOW_THREADS; + g_object_set_property(obj, pspec->name, &value); + g_value_unset(&value); + Py_END_ALLOW_THREADS; + + return TRUE; +} + +PYGLIB_DEFINE_TYPE("gi._gobject.GProps", PyGProps_Type, PyGProps); + +static int +PyGProps_setattro(PyGProps *self, PyObject *attr, PyObject *pvalue) +{ + GParamSpec *pspec; + char *attr_name, *property_name; + GObject *obj; + int ret = -1; + + if (pvalue == NULL) { + PyErr_SetString(PyExc_TypeError, "properties cannot be " + "deleted"); + return -1; + } + + attr_name = PYGLIB_PyUnicode_AsString(attr); + if (!attr_name) { + PyErr_Clear(); + return PyObject_GenericSetAttr((PyObject *)self, attr, pvalue); + } + + if (!self->pygobject) { + PyErr_SetString(PyExc_TypeError, + "cannot set GOject properties without an instance"); + return -1; + } + + obj = self->pygobject->obj; + + property_name = g_strdup(attr_name); + canonicalize_key(property_name); + + /* g_object_class_find_property recurses through the class hierarchy, + * so the resulting pspec tells us the owner_type that owns the property + * we're dealing with. */ + pspec = g_object_class_find_property(G_OBJECT_GET_CLASS(obj), + property_name); + g_free(property_name); + if (!pspec) { + return PyObject_GenericSetAttr((PyObject *)self, attr, pvalue); + } + if (!pyg_gtype_is_custom (pspec->owner_type)) { + /* This GType is not implemented in Python: see if we can set the + * property via gi. */ + ret = pygi_set_property_value (self->pygobject, pspec, pvalue); + if (ret == 0) + return 0; + else if (ret == -1 && PyErr_Occurred()) + return -1; + } + + /* This GType is implemented in Python, or we failed to set it via gi: + * do a straightforward set. */ + if (!set_property_from_pspec(obj, pspec, pvalue)) + return -1; + + return 0; +} + +static int +pygobject_props_traverse(PyGProps *self, visitproc visit, void *arg) +{ + if (self->pygobject && visit((PyObject *) self->pygobject, arg) < 0) + return -1; + return 0; +} + +static PyObject* +pygobject_props_get_iter(PyGProps *self) +{ + PyGPropsIter *iter; + GObjectClass *class; + + iter = PyObject_NEW(PyGPropsIter, &PyGPropsIter_Type); + class = g_type_class_ref(self->gtype); + iter->props = g_object_class_list_properties(class, &iter->n_props); + iter->index = 0; + g_type_class_unref(class); + return (PyObject *) iter; +} + +static PyObject* +pygobject_props_dir(PyGProps *self) +{ + PyObject *ret; + GObjectClass *class; + + class = g_type_class_ref (self->gtype); + ret = build_parameter_list (class); + g_type_class_unref (class); + + return ret; +} + +static PyMethodDef pygobject_props_methods[] = { + { "__dir__", (PyCFunction)pygobject_props_dir, METH_NOARGS}, + { NULL, NULL, 0} +}; + + +static Py_ssize_t +PyGProps_length(PyGProps *self) +{ + GObjectClass *class; + GParamSpec **props; + guint n_props; + + class = g_type_class_ref(self->gtype); + props = g_object_class_list_properties(class, &n_props); + g_type_class_unref(class); + g_free(props); + + return (Py_ssize_t)n_props; +} + +static PySequenceMethods _PyGProps_as_sequence = { + (lenfunc) PyGProps_length, + 0, + 0, + 0, + 0, + 0, + 0 +}; + +PYGLIB_DEFINE_TYPE("gi._gobject.GPropsDescr", PyGPropsDescr_Type, PyObject); + +static PyObject * +pyg_props_descr_descr_get(PyObject *self, PyObject *obj, PyObject *type) +{ + PyGProps *gprops; + + gprops = PyObject_GC_New(PyGProps, &PyGProps_Type); + if (obj == NULL || obj == Py_None) { + gprops->pygobject = NULL; + gprops->gtype = pyg_type_from_object(type); + } else { + if (!PyObject_IsInstance(obj, (PyObject *) &PyGObject_Type)) { + PyErr_SetString(PyExc_TypeError, "cannot use GObject property" + " descriptor on non-GObject instances"); + return NULL; + } + Py_INCREF(obj); + gprops->pygobject = (PyGObject *) obj; + gprops->gtype = pyg_type_from_object(obj); + } + return (PyObject *) gprops; +} + +/** + * pygobject_register_class: + * @dict: the module dictionary. A reference to the type will be stored here. + * @type_name: not used ? + * @gtype: the GType of the GObject subclass. + * @type: the Python type object for this wrapper. + * @static_bases: a tuple of Python type objects that are the bases of + * this type + * + * This function is used to register a Python type as the wrapper for + * a particular GObject subclass. It will also insert a reference to + * the wrapper class into the module dictionary passed as a reference, + * which simplifies initialisation. + */ +void +pygobject_register_class(PyObject *dict, const gchar *type_name, + GType gtype, PyTypeObject *type, + PyObject *static_bases) +{ + PyObject *o; + const char *class_name, *s; + PyObject *runtime_bases; + PyObject *bases_list, *bases, *mod_name; + int i; + + class_name = type->tp_name; + s = strrchr(class_name, '.'); + if (s != NULL) + class_name = s + 1; + + runtime_bases = pyg_type_get_bases(gtype); + if (static_bases) { + PyTypeObject *py_parent_type = (PyTypeObject *) PyTuple_GET_ITEM(static_bases, 0); + bases_list = PySequence_List(static_bases); + /* we start at index 1 because we want to skip the primary + * base, otherwise we might get MRO conflict */ + for (i = 1; i < PyTuple_GET_SIZE(runtime_bases); ++i) + { + PyObject *base = PyTuple_GET_ITEM(runtime_bases, i); + int contains = PySequence_Contains(bases_list, base); + if (contains < 0) + PyErr_Print(); + else if (!contains) { + if (!PySequence_Contains(py_parent_type->tp_mro, base)) { +#if 0 + g_message("Adding missing base %s to type %s", + ((PyTypeObject *)base)->tp_name, type->tp_name); +#endif + PyList_Append(bases_list, base); + } + } + } + bases = PySequence_Tuple(bases_list); + Py_DECREF(bases_list); + Py_DECREF(runtime_bases); + } else + bases = runtime_bases; + + Py_TYPE(type) = PyGObject_MetaType; + type->tp_bases = bases; + if (G_LIKELY(bases)) { + type->tp_base = (PyTypeObject *)PyTuple_GetItem(bases, 0); + Py_INCREF(type->tp_base); + } + + pygobject_inherit_slots(type, bases, TRUE); + + if (PyType_Ready(type) < 0) { + g_warning ("couldn't make the type `%s' ready", type->tp_name); + return; + } + + /* Set type.__module__ to the name of the module, + * otherwise it'll default to 'gobject', see #376099 + */ + s = strrchr(type->tp_name, '.'); + if (s != NULL) { + mod_name = PYGLIB_PyUnicode_FromStringAndSize(type->tp_name, (int)(s - type->tp_name)); + PyDict_SetItemString(type->tp_dict, "__module__", mod_name); + Py_DECREF(mod_name); + } + + if (gtype) { + o = pyg_type_wrapper_new(gtype); + PyDict_SetItemString(type->tp_dict, "__gtype__", o); + Py_DECREF(o); + + /* stash a pointer to the python class with the GType */ + Py_INCREF(type); + g_type_set_qdata(gtype, pygobject_class_key, type); + } + + /* set up __doc__ descriptor on type */ + PyDict_SetItemString(type->tp_dict, "__doc__", + pyg_object_descr_doc_get()); + + PyDict_SetItemString(dict, (char *)class_name, (PyObject *)type); +} + +static void +pyg_toggle_notify (gpointer data, GObject *object, gboolean is_last_ref) +{ + PyGObject *self; + PyGILState_STATE state; + + state = pyglib_gil_state_ensure(); + + /* Avoid thread safety problems by using qdata for wrapper retrieval + * instead of the user data argument. + * See: https://bugzilla.gnome.org/show_bug.cgi?id=709223 + */ + self = (PyGObject *)g_object_get_qdata (object, pygobject_wrapper_key); + if (self) { + if (is_last_ref) + Py_DECREF(self); + else + Py_INCREF(self); + } + + pyglib_gil_state_release(state); +} + + /* Called when the inst_dict is first created; switches the + reference counting strategy to start using toggle ref to keep the + wrapper alive while the GObject lives. In contrast, while + inst_dict was NULL the python wrapper is allowed to die at + will and is recreated on demand. */ +static inline void +pygobject_switch_to_toggle_ref(PyGObject *self) +{ + g_assert(self->obj->ref_count >= 1); + + if (self->private_flags.flags & PYGOBJECT_USING_TOGGLE_REF) + return; /* already using toggle ref */ + self->private_flags.flags |= PYGOBJECT_USING_TOGGLE_REF; + /* Note that add_toggle_ref will never immediately call back into + pyg_toggle_notify */ + Py_INCREF((PyObject *) self); + g_object_add_toggle_ref(self->obj, pyg_toggle_notify, NULL); + g_object_unref(self->obj); +} + +/* Called when an custom gobject is initalized via g_object_new instead of + its constructor. The next time the wrapper is access via + pygobject_new_full it will sink the floating reference instead of + adding a new reference and causing a leak */ + +void +pygobject_ref_float(PyGObject *self) +{ + /* should only be floated once */ + g_assert(!(self->private_flags.flags & PYGOBJECT_IS_FLOATING_REF)); + + self->private_flags.flags |= PYGOBJECT_IS_FLOATING_REF; +} + +/* Called by gobject_new_full, if the floating flag is set remove it, otherwise + ref the pyobject */ +void +pygobject_ref_sink(PyGObject *self) +{ + if (self->private_flags.flags & PYGOBJECT_IS_FLOATING_REF) + self->private_flags.flags &= ~PYGOBJECT_IS_FLOATING_REF; + else + Py_INCREF ( (PyObject *) self); +} + +/** + * pygobject_register_wrapper: + * @self: the wrapper instance + * + * In the constructor of PyGTK wrappers, this function should be + * called after setting the obj member. It will tie the wrapper + * instance to the GObject so that the same wrapper instance will + * always be used for this GObject instance. + */ +void +pygobject_register_wrapper(PyObject *self) +{ + PyGObject *gself; + + g_return_if_fail(self != NULL); + g_return_if_fail(PyObject_TypeCheck(self, &PyGObject_Type)); + + gself = (PyGObject *)self; + + g_assert(gself->obj->ref_count >= 1); + /* save wrapper pointer so we can access it later */ + g_object_set_qdata_full(gself->obj, pygobject_wrapper_key, gself, NULL); + if (gself->inst_dict) + pygobject_switch_to_toggle_ref(gself); +} + +static PyObject * +pyg_type_get_bases(GType gtype) +{ + GType *interfaces, parent_type, interface_type; + guint n_interfaces; + PyTypeObject *py_parent_type, *py_interface_type; + PyObject *bases; + int i; + + if (G_UNLIKELY(gtype == G_TYPE_OBJECT)) + return NULL; + + /* Lookup the parent type */ + parent_type = g_type_parent(gtype); + py_parent_type = pygobject_lookup_class(parent_type); + interfaces = g_type_interfaces(gtype, &n_interfaces); + bases = PyTuple_New(n_interfaces + 1); + /* We will always put the parent at the first position in bases */ + Py_INCREF(py_parent_type); /* PyTuple_SetItem steals a reference */ + PyTuple_SetItem(bases, 0, (PyObject *) py_parent_type); + + /* And traverse interfaces */ + if (n_interfaces) { + for (i = 0; i < n_interfaces; i++) { + interface_type = interfaces[i]; + py_interface_type = pygobject_lookup_class(interface_type); + Py_INCREF(py_interface_type); /* PyTuple_SetItem steals a reference */ + PyTuple_SetItem(bases, i + 1, (PyObject *) py_interface_type); + } + } + g_free(interfaces); + return bases; +} + +/** + * pygobject_new_with_interfaces + * @gtype: the GType of the GObject subclass. + * + * Creates a new PyTypeObject from the given GType with interfaces attached in + * bases. + * + * Returns: a PyTypeObject for the new type or NULL if it couldn't be created + */ +static PyTypeObject * +pygobject_new_with_interfaces(GType gtype) +{ + PyGILState_STATE state; + PyObject *o; + PyTypeObject *type; + PyObject *dict; + PyTypeObject *py_parent_type; + PyObject *bases; + + state = pyglib_gil_state_ensure(); + + bases = pyg_type_get_bases(gtype); + py_parent_type = (PyTypeObject *) PyTuple_GetItem(bases, 0); + + dict = PyDict_New(); + + o = pyg_type_wrapper_new(gtype); + PyDict_SetItemString(dict, "__gtype__", o); + Py_DECREF(o); + + /* set up __doc__ descriptor on type */ + PyDict_SetItemString(dict, "__doc__", pyg_object_descr_doc_get()); + + /* Something special to point out that it's not accessible through + * gi.repository */ + o = PYGLIB_PyUnicode_FromString ("__gi__"); + PyDict_SetItemString (dict, "__module__", o); + Py_DECREF (o); + + type = (PyTypeObject*)PyObject_CallFunction((PyObject *) Py_TYPE(py_parent_type), + "sNN", g_type_name (gtype), bases, dict); + + if (type == NULL) { + PyErr_Print(); + pyglib_gil_state_release(state); + return NULL; + } + + /* Workaround python tp_(get|set)attr slot inheritance bug. + * Fixes bug #144135. */ + if (!type->tp_getattr && py_parent_type->tp_getattr) { + type->tp_getattro = NULL; + type->tp_getattr = py_parent_type->tp_getattr; + } + if (!type->tp_setattr && py_parent_type->tp_setattr) { + type->tp_setattro = NULL; + type->tp_setattr = py_parent_type->tp_setattr; + } + /* override more python stupid hacks behind our back */ + type->tp_dealloc = py_parent_type->tp_dealloc; + type->tp_alloc = py_parent_type->tp_alloc; + type->tp_free = py_parent_type->tp_free; + type->tp_traverse = py_parent_type->tp_traverse; + type->tp_clear = py_parent_type->tp_clear; + + pygobject_inherit_slots(type, bases, FALSE); + + if (PyType_Ready(type) < 0) { + g_warning ("couldn't make the type `%s' ready", type->tp_name); + pyglib_gil_state_release(state); + return NULL; + } + + /* stash a pointer to the python class with the GType */ + Py_INCREF(type); + g_type_set_qdata(gtype, pygobject_class_key, type); + + pyglib_gil_state_release(state); + + return type; +} + +/* Pick appropriate value for given slot (at slot_offset inside + * PyTypeObject structure). It must be a pointer, e.g. a pointer to a + * function. We use the following heuristic: + * + * - Scan all types listed as bases of the type. + * - If for exactly one base type slot value is non-NULL and + * different from that of 'object' and 'GObject', set current type + * slot into that value. + * - Otherwise (if there is more than one such base type or none at + * all) don't touch it and live with Python default. + * + * The intention here is to propagate slot from custom wrappers to + * wrappers created at runtime when appropriate. We prefer to be on + * the safe side, so if there is potential collision (more than one + * custom slot value), we discard custom overrides altogether. + * + * When registering type with pygobject_register_class(), i.e. a type + * that has been manually created (likely with Codegen help), + * `check_for_present' should be set to TRUE. In this case, the + * function will never overwrite any non-NULL slots already present in + * the type. If `check_for_present' is FALSE, such non-NULL slots are + * though to be set by Python interpreter and so will be overwritten + * if heuristic above says so. + */ +static void +pygobject_inherit_slots(PyTypeObject *type, PyObject *bases, gboolean check_for_present) +{ + static int slot_offsets[] = { offsetof(PyTypeObject, tp_richcompare), +#if PY_VERSION_HEX < 0x03000000 + offsetof(PyTypeObject, tp_compare), +#endif + offsetof(PyTypeObject, tp_richcompare), + offsetof(PyTypeObject, tp_hash), + offsetof(PyTypeObject, tp_iter), + offsetof(PyTypeObject, tp_repr), + offsetof(PyTypeObject, tp_str), + offsetof(PyTypeObject, tp_print) }; + int i; + + /* Happens when registering gobject.GObject itself, at least. */ + if (!bases) + return; + + for (i = 0; i < G_N_ELEMENTS(slot_offsets); ++i) + pygobject_find_slot_for(type, bases, slot_offsets[i], check_for_present); +} + +static void +pygobject_find_slot_for(PyTypeObject *type, PyObject *bases, int slot_offset, + gboolean check_for_present) +{ +#define TYPE_SLOT(type) (* (void **) (((char *) (type)) + slot_offset)) + + void *found_slot = NULL; + int num_bases = PyTuple_Size(bases); + int i; + + if (check_for_present && TYPE_SLOT(type) != NULL) { + /* We are requested to check if there is any custom slot value + * in this type already and there actually is. Don't + * overwrite it. + */ + return; + } + + for (i = 0; i < num_bases; ++i) { + PyTypeObject *base_type = (PyTypeObject *) PyTuple_GetItem(bases, i); + void *slot = TYPE_SLOT(base_type); + + if (slot == NULL) + continue; + if (slot == TYPE_SLOT(&PyGObject_Type) || + slot == TYPE_SLOT(&PyBaseObject_Type)) + continue; + + if (found_slot != NULL && found_slot != slot) { + /* We have a conflict: more than one base use different + * custom slots. To be on the safe side, we bail out. + */ + return; + } + + found_slot = slot; + } + + /* Only perform the final assignment if at least one base has a + * custom value. Otherwise just leave this type's slot untouched. + */ + if (found_slot != NULL) + TYPE_SLOT(type) = found_slot; + +#undef TYPE_SLOT +} + +/** + * pygobject_lookup_class: + * @gtype: the GType of the GObject subclass. + * + * This function looks up the wrapper class used to represent + * instances of a GObject represented by @gtype. If no wrapper class + * or interface has been registered for the given GType, then a new + * type will be created. + * + * Returns: The wrapper class for the GObject or NULL if the + * GType has no registered type and a new type couldn't be created + */ +PyTypeObject * +pygobject_lookup_class(GType gtype) +{ + PyTypeObject *py_type; + + if (gtype == G_TYPE_INTERFACE) + return &PyGInterface_Type; + + py_type = g_type_get_qdata(gtype, pygobject_class_key); + if (py_type == NULL) { + py_type = g_type_get_qdata(gtype, pyginterface_type_key); + + if (py_type == NULL) + py_type = (PyTypeObject *)pygi_type_import_by_g_type(gtype); + + if (py_type == NULL) { + py_type = pygobject_new_with_interfaces(gtype); + g_type_set_qdata(gtype, pyginterface_type_key, py_type); + } + } + + return py_type; +} + +/** + * pygobject_new_full: + * @obj: a GObject instance. + * @steal: whether to steal a ref from the GObject or add (sink) a new one. + * @g_class: the GObjectClass + * + * This function gets a reference to a wrapper for the given GObject + * instance. If a wrapper has already been created, a new reference + * to that wrapper will be returned. Otherwise, a wrapper instance + * will be created. + * + * Returns: a reference to the wrapper for the GObject. + */ +PyObject * +pygobject_new_full(GObject *obj, gboolean steal, gpointer g_class) +{ + PyGObject *self; + + if (obj == NULL) { + Py_RETURN_NONE; + } + + /* If the GObject already has a PyObject wrapper stashed in its qdata, re-use it. + */ + self = (PyGObject *)g_object_get_qdata(obj, pygobject_wrapper_key); + if (self != NULL) { + /* Note the use of "pygobject_ref_sink" here only deals with PyObject + * wrapper ref counts and has nothing to do with GObject. + */ + pygobject_ref_sink(self); + + /* If steal is true, we also want to decref the incoming GObjects which + * already have a Python wrapper because the wrapper is already holding a + * strong reference. + */ + if (steal) + g_object_unref (obj); + + } else { + /* create wrapper */ + PyGObjectData *inst_data = pyg_object_peek_inst_data(obj); + PyTypeObject *tp; + if (inst_data) + tp = inst_data->type; + else { + if (g_class) + tp = pygobject_lookup_class(G_OBJECT_CLASS_TYPE(g_class)); + else + tp = pygobject_lookup_class(G_OBJECT_TYPE(obj)); + } + g_assert(tp != NULL); + + /* need to bump type refcount if created with + pygobject_new_with_interfaces(). fixes bug #141042 */ + if (tp->tp_flags & Py_TPFLAGS_HEAPTYPE) + Py_INCREF(tp); + self = PyObject_GC_New(PyGObject, tp); + if (self == NULL) + return NULL; + self->inst_dict = NULL; + self->weakreflist = NULL; + self->private_flags.flags = 0; + self->obj = obj; + + /* If we are not stealing a ref or the object is floating, + * add a regular ref or sink the object. */ + if (g_object_is_floating (obj)) + self->private_flags.flags |= PYGOBJECT_GOBJECT_WAS_FLOATING; + if (!steal || self->private_flags.flags & PYGOBJECT_GOBJECT_WAS_FLOATING) + g_object_ref_sink (obj); + + pygobject_register_wrapper((PyObject *)self); + PyObject_GC_Track((PyObject *)self); + } + + return (PyObject *)self; +} + + +PyObject * +pygobject_new(GObject *obj) +{ + return pygobject_new_full(obj, + /*steal=*/FALSE, + NULL); +} + +static void +pygobject_unwatch_closure(gpointer data, GClosure *closure) +{ + PyGObjectData *inst_data = data; + + inst_data->closures = g_slist_remove (inst_data->closures, closure); +} + +/** + * pygobject_watch_closure: + * @self: a GObject wrapper instance + * @closure: a GClosure to watch + * + * Adds a closure to the list of watched closures for the wrapper. + * The closure must be one returned by pyg_closure_new(). When the + * cycle GC traverses the wrapper instance, it will enumerate the + * references to Python objects stored in watched closures. If the + * cycle GC tells the wrapper to clear itself, the watched closures + * will be invalidated. + */ +void +pygobject_watch_closure(PyObject *self, GClosure *closure) +{ + PyGObject *gself; + PyGObjectData *data; + + g_return_if_fail(self != NULL); + g_return_if_fail(PyObject_TypeCheck(self, &PyGObject_Type)); + g_return_if_fail(closure != NULL); + + gself = (PyGObject *)self; + data = pygobject_get_inst_data(gself); + g_return_if_fail(g_slist_find(data->closures, closure) == NULL); + data->closures = g_slist_prepend(data->closures, closure); + g_closure_add_invalidate_notifier(closure, data, pygobject_unwatch_closure); +} + + +/* -------------- PyGObject behaviour ----------------- */ + +PYGLIB_DEFINE_TYPE("gi._gobject.GObject", PyGObject_Type, PyGObject); + +static void +pygobject_dealloc(PyGObject *self) +{ + /* Untrack must be done first. This is because followup calls such as + * ClearWeakRefs could call into Python and cause new allocations to + * happen, which could in turn could trigger the garbage collector, + * which would then get confused as it is tracking this half-deallocated + * object. */ + PyObject_GC_UnTrack((PyObject *)self); + + PyObject_ClearWeakRefs((PyObject *)self); + /* this forces inst_data->type to be updated, which could prove + * important if a new wrapper has to be created and it is of a + * unregistered type */ + pygobject_get_inst_data(self); + pygobject_clear(self); + /* the following causes problems with subclassed types */ + /* Py_TYPE(self)->tp_free((PyObject *)self); */ + PyObject_GC_Del(self); +} + +static PyObject* +pygobject_richcompare(PyObject *self, PyObject *other, int op) +{ + int isinst; + + isinst = PyObject_IsInstance(self, (PyObject*)&PyGObject_Type); + if (isinst == -1) + return NULL; + if (!isinst) { + Py_INCREF(Py_NotImplemented); + return Py_NotImplemented; + } + isinst = PyObject_IsInstance(other, (PyObject*)&PyGObject_Type); + if (isinst == -1) + return NULL; + if (!isinst) { + Py_INCREF(Py_NotImplemented); + return Py_NotImplemented; + } + + return _pyglib_generic_ptr_richcompare(((PyGObject*)self)->obj, + ((PyGObject*)other)->obj, + op); +} + +static long +pygobject_hash(PyGObject *self) +{ + return (long)self->obj; +} + +static PyObject * +pygobject_repr(PyGObject *self) +{ + PyObject *module, *repr; + gchar *module_str, *namespace; + + module = PyObject_GetAttrString ((PyObject *)self, "__module__"); + if (module == NULL) + return NULL; + + if (!PYGLIB_PyUnicode_Check (module)) { + Py_DECREF (module); + return NULL; + } + + module_str = PYGLIB_PyUnicode_AsString (module); + namespace = g_strrstr (module_str, "."); + if (namespace == NULL) { + namespace = module_str; + } else { + namespace += 1; + } + + repr = PYGLIB_PyUnicode_FromFormat ("<%s.%s object at %p (%s at %p)>", + namespace, Py_TYPE (self)->tp_name, self, + self->obj ? G_OBJECT_TYPE_NAME (self->obj) : "uninitialized", + self->obj); + Py_DECREF (module); + return repr; +} + + +static int +pygobject_traverse(PyGObject *self, visitproc visit, void *arg) +{ + int ret = 0; + GSList *tmp; + PyGObjectData *data = pygobject_get_inst_data(self); + + if (self->inst_dict) ret = visit(self->inst_dict, arg); + if (ret != 0) return ret; + + if (data) { + + for (tmp = data->closures; tmp != NULL; tmp = tmp->next) { + PyGClosure *closure = tmp->data; + + if (closure->callback) ret = visit(closure->callback, arg); + if (ret != 0) return ret; + + if (closure->extra_args) ret = visit(closure->extra_args, arg); + if (ret != 0) return ret; + + if (closure->swap_data) ret = visit(closure->swap_data, arg); + if (ret != 0) return ret; + } + } + return ret; +} + +static inline int +pygobject_clear(PyGObject *self) +{ + if (self->obj) { + g_object_set_qdata_full(self->obj, pygobject_wrapper_key, NULL, NULL); + if (self->inst_dict) { + g_object_remove_toggle_ref(self->obj, pyg_toggle_notify, NULL); + self->private_flags.flags &= ~PYGOBJECT_USING_TOGGLE_REF; + } else { + Py_BEGIN_ALLOW_THREADS; + g_object_unref(self->obj); + Py_END_ALLOW_THREADS; + } + self->obj = NULL; + } + Py_CLEAR(self->inst_dict); + return 0; +} + +static void +pygobject_free(PyObject *op) +{ + PyObject_GC_Del(op); +} + +gboolean +pygobject_prepare_construct_properties(GObjectClass *class, PyObject *kwargs, + guint *n_params, GParameter **params) +{ + *n_params = 0; + *params = NULL; + + if (kwargs) { + Py_ssize_t pos = 0; + PyObject *key; + PyObject *value; + + *params = g_new0(GParameter, PyDict_Size(kwargs)); + while (PyDict_Next(kwargs, &pos, &key, &value)) { + GParamSpec *pspec; + GParameter *param = &(*params)[*n_params]; + const gchar *key_str = PYGLIB_PyUnicode_AsString(key); + + pspec = g_object_class_find_property(class, key_str); + if (!pspec) { + PyErr_Format(PyExc_TypeError, + "gobject `%s' doesn't support property `%s'", + G_OBJECT_CLASS_NAME(class), key_str); + return FALSE; + } + g_value_init(¶m->value, G_PARAM_SPEC_VALUE_TYPE(pspec)); + if (pyg_param_gvalue_from_pyobject(¶m->value, value, pspec) < 0) { + PyErr_Format(PyExc_TypeError, + "could not convert value for property `%s' from %s to %s", + key_str, Py_TYPE(value)->tp_name, + g_type_name(G_PARAM_SPEC_VALUE_TYPE(pspec))); + return FALSE; + } + param->name = g_strdup(key_str); + ++(*n_params); + } + } + return TRUE; +} + +/* ---------------- PyGObject methods ----------------- */ + +static int +pygobject_init(PyGObject *self, PyObject *args, PyObject *kwargs) +{ + GType object_type; + guint n_params = 0, i; + GParameter *params = NULL; + GObjectClass *class; + + /* Only do GObject creation and property setting if the GObject hasn't + * already been created. The case where self->obj already exists can occur + * when C constructors are called directly (Gtk.Button.new_with_label) + * and we are simply wrapping the result with a PyGObject. + * In these cases we want to ignore any keyword arguments passed along + * to __init__ and simply return. + * + * See: https://bugzilla.gnome.org/show_bug.cgi?id=705810 + */ + if (self->obj != NULL) + return 0; + + if (!PyArg_ParseTuple(args, ":GObject.__init__", NULL)) + return -1; + + object_type = pyg_type_from_object((PyObject *)self); + if (!object_type) + return -1; + + if (G_TYPE_IS_ABSTRACT(object_type)) { + PyErr_Format(PyExc_TypeError, "cannot create instance of abstract " + "(non-instantiable) type `%s'", g_type_name(object_type)); + return -1; + } + + if ((class = g_type_class_ref (object_type)) == NULL) { + PyErr_SetString(PyExc_TypeError, + "could not get a reference to type class"); + return -1; + } + + if (!pygobject_prepare_construct_properties (class, kwargs, &n_params, ¶ms)) + goto cleanup; + + if (pygobject_constructv(self, n_params, params)) + PyErr_SetString(PyExc_RuntimeError, "could not create object"); + + cleanup: + for (i = 0; i < n_params; i++) { + g_free((gchar *) params[i].name); + g_value_unset(¶ms[i].value); + } + g_free(params); + g_type_class_unref(class); + + return (self->obj) ? 0 : -1; +} + +#define CHECK_GOBJECT(self) \ + if (!G_IS_OBJECT(self->obj)) { \ + PyErr_Format(PyExc_TypeError, \ + "object at %p of type %s is not initialized", \ + self, Py_TYPE(self)->tp_name); \ + return NULL; \ + } + +static PyObject * +pygobject_get_property (PyGObject *self, PyObject *args) +{ + gchar *param_name; + + if (!PyArg_ParseTuple (args, "s:GObject.get_property", ¶m_name)) { + return NULL; + } + + CHECK_GOBJECT(self); + + return pygi_get_property_value_by_name (self, param_name); +} + +static PyObject * +pygobject_get_properties(PyGObject *self, PyObject *args) +{ + int len, i; + PyObject *tuple; + + if ((len = PyTuple_Size(args)) < 1) { + PyErr_SetString(PyExc_TypeError, "requires at least one argument"); + return NULL; + } + + tuple = PyTuple_New(len); + for (i = 0; i < len; i++) { + PyObject *py_property = PyTuple_GetItem(args, i); + gchar *property_name; + PyObject *item; + + if (!PYGLIB_PyUnicode_Check(py_property)) { + PyErr_SetString(PyExc_TypeError, + "Expected string argument for property."); + goto fail; + } + + property_name = PYGLIB_PyUnicode_AsString(py_property); + item = pygi_get_property_value_by_name (self, property_name); + PyTuple_SetItem (tuple, i, item); + } + + return tuple; + +fail: + Py_DECREF (tuple); + return NULL; +} + +static PyObject * +pygobject_set_property(PyGObject *self, PyObject *args) +{ + gchar *param_name; + GParamSpec *pspec; + PyObject *pvalue; + int ret = -1; + + if (!PyArg_ParseTuple(args, "sO:GObject.set_property", ¶m_name, + &pvalue)) + return NULL; + + CHECK_GOBJECT(self); + + pspec = g_object_class_find_property(G_OBJECT_GET_CLASS(self->obj), + param_name); + if (!pspec) { + PyErr_Format(PyExc_TypeError, + "object of type `%s' does not have property `%s'", + g_type_name(G_OBJECT_TYPE(self->obj)), param_name); + return NULL; + } + + ret = pygi_set_property_value (self, pspec, pvalue); + if (ret == 0) + goto done; + else if (PyErr_Occurred()) + return NULL; + + if (!set_property_from_pspec(self->obj, pspec, pvalue)) + return NULL; + +done: + + Py_INCREF(Py_None); + return Py_None; +} + +static PyObject * +pygobject_set_properties(PyGObject *self, PyObject *args, PyObject *kwargs) +{ + GObjectClass *class; + Py_ssize_t pos; + PyObject *value; + PyObject *key; + PyObject *result = NULL; + + CHECK_GOBJECT(self); + + class = G_OBJECT_GET_CLASS(self->obj); + + g_object_freeze_notify (G_OBJECT(self->obj)); + pos = 0; + + while (kwargs && PyDict_Next (kwargs, &pos, &key, &value)) { + gchar *key_str = PYGLIB_PyUnicode_AsString(key); + GParamSpec *pspec; + int ret = -1; + + pspec = g_object_class_find_property(class, key_str); + if (!pspec) { + gchar buf[512]; + + g_snprintf(buf, sizeof(buf), + "object `%s' doesn't support property `%s'", + g_type_name(G_OBJECT_TYPE(self->obj)), key_str); + PyErr_SetString(PyExc_TypeError, buf); + goto exit; + } + + ret = pygi_set_property_value (self, pspec, value); + if (ret != 0) { + /* Non-zero return code means that either an error occured ...*/ + if (PyErr_Occurred()) + goto exit; + + /* ... or the property couldn't be found , so let's try the default + * call. */ + if (!set_property_from_pspec(G_OBJECT(self->obj), pspec, value)) + goto exit; + } + } + + result = Py_None; + + exit: + g_object_thaw_notify (G_OBJECT(self->obj)); + Py_XINCREF(result); + return result; +} + +/* custom closure for gobject bindings */ +static void +pygbinding_closure_invalidate(gpointer data, GClosure *closure) +{ + PyGClosure *pc = (PyGClosure *)closure; + PyGILState_STATE state; + + state = pyglib_gil_state_ensure(); + Py_XDECREF(pc->callback); + Py_XDECREF(pc->extra_args); + pyglib_gil_state_release(state); + + pc->callback = NULL; + pc->extra_args = NULL; +} + +static void +pygbinding_marshal (GClosure *closure, + GValue *return_value, + guint n_param_values, + const GValue *param_values, + gpointer invocation_hint, + gpointer marshal_data) +{ + PyGILState_STATE state; + PyGClosure *pc = (PyGClosure *)closure; + PyObject *params, *ret; + GValue *out_value; + + state = pyglib_gil_state_ensure(); + + /* construct Python tuple for the parameter values */ + params = PyTuple_New(2); + PyTuple_SetItem (params, 0, pyg_value_as_pyobject(¶m_values[0], FALSE)); + PyTuple_SetItem (params, 1, pyg_value_as_pyobject(¶m_values[1], FALSE)); + + /* params passed to function may have extra arguments */ + if (pc->extra_args) { + PyObject *tuple = params; + params = PySequence_Concat(tuple, pc->extra_args); + Py_DECREF(tuple); + } + ret = PyObject_CallObject(pc->callback, params); + if (!ret) { + PyErr_Print (); + goto out; + } else if (ret == Py_None) { + g_value_set_boolean (return_value, FALSE); + goto out; + } + + out_value = g_value_get_boxed (¶m_values[2]); + if (pyg_value_from_pyobject (out_value, ret) != 0) { + PyErr_SetString (PyExc_ValueError, "can't convert value"); + PyErr_Print (); + g_value_set_boolean (return_value, FALSE); + } else { + g_value_set_boolean (return_value, TRUE); + } + + Py_DECREF(ret); + +out: + Py_DECREF(params); + pyglib_gil_state_release(state); +} + +static GClosure * +pygbinding_closure_new (PyObject *callback, PyObject *extra_args) +{ + GClosure *closure; + + g_return_val_if_fail(callback != NULL, NULL); + closure = g_closure_new_simple(sizeof(PyGClosure), NULL); + g_closure_add_invalidate_notifier(closure, NULL, pygbinding_closure_invalidate); + g_closure_set_marshal(closure, pygbinding_marshal); + Py_INCREF(callback); + ((PyGClosure *)closure)->callback = callback; + if (extra_args && extra_args != Py_None) { + Py_INCREF(extra_args); + if (!PyTuple_Check(extra_args)) { + PyObject *tmp = PyTuple_New(1); + PyTuple_SetItem(tmp, 0, extra_args); + extra_args = tmp; + } + ((PyGClosure *)closure)->extra_args = extra_args; + } + return closure; +} + +static PyObject * +pygobject_bind_property(PyGObject *self, PyObject *args) +{ + gchar *source_name, *target_name; + gchar *source_canon, *target_canon; + PyObject *target, *source_repr, *target_repr; + PyObject *transform_to, *transform_from, *user_data = NULL; + GBinding *binding; + GBindingFlags flags = G_BINDING_DEFAULT; + GClosure *to_closure = NULL, *from_closure = NULL; + + transform_from = NULL; + transform_to = NULL; + + if (!PyArg_ParseTuple(args, "sOs|iOOO:GObject.bind_property", + &source_name, &target, &target_name, &flags, + &transform_to, &transform_from, &user_data)) + return NULL; + + CHECK_GOBJECT(self); + if (!PyObject_TypeCheck(target, &PyGObject_Type)) { + PyErr_SetString(PyExc_TypeError, "Second argument must be a GObject"); + return NULL; + } + + if (transform_to && transform_to != Py_None) { + if (!PyCallable_Check (transform_to)) { + PyErr_SetString (PyExc_TypeError, + "transform_to must be callable or None"); + return NULL; + } + to_closure = pygbinding_closure_new (transform_to, user_data); + } + + if (transform_from && transform_from != Py_None) { + if (!PyCallable_Check (transform_from)) { + PyErr_SetString (PyExc_TypeError, + "transform_from must be callable or None"); + return NULL; + } + from_closure = pygbinding_closure_new (transform_from, user_data); + } + + /* Canonicalize underscores to hyphens. Note the results must be freed. */ + source_canon = g_strdelimit(g_strdup(source_name), "_", '-'); + target_canon = g_strdelimit(g_strdup(target_name), "_", '-'); + + binding = g_object_bind_property_with_closures (G_OBJECT(self->obj), source_canon, + pygobject_get(target), target_canon, + flags, to_closure, from_closure); + g_free(source_canon); + g_free(target_canon); + source_canon = target_canon = NULL; + + if (binding == NULL) { + source_repr = PyObject_Repr((PyObject*)self); + target_repr = PyObject_Repr(target); + PyErr_Format(PyExc_TypeError, "Cannot create binding from %s.%s to %s.%s", + PYGLIB_PyUnicode_AsString(source_repr), source_name, + PYGLIB_PyUnicode_AsString(target_repr), target_name); + Py_DECREF(source_repr); + Py_DECREF(target_repr); + return NULL; + } + + return pygobject_new (G_OBJECT (binding)); +} + +static PyObject * +connect_helper(PyGObject *self, gchar *name, PyObject *callback, PyObject *extra_args, PyObject *object, gboolean after) +{ + guint sigid; + GQuark detail = 0; + GClosure *closure = NULL; + gulong handlerid; + GSignalQuery query_info; + + if (!g_signal_parse_name(name, G_OBJECT_TYPE(self->obj), + &sigid, &detail, TRUE)) { + PyObject *repr = PyObject_Repr((PyObject*)self); + PyErr_Format(PyExc_TypeError, "%s: unknown signal name: %s", + PYGLIB_PyUnicode_AsString(repr), + name); + Py_DECREF(repr); + return NULL; + } + + if (object && !PyObject_TypeCheck (object, &PyGObject_Type)) { + if (PyErr_WarnEx (PyGIDeprecationWarning, + "Using non GObject arguments for connect_object() is deprecated, use: " + "connect_data(signal, callback, data, connect_flags=GObject.ConnectFlags.SWAPPED)", + 1)) { + return NULL; + } + } + + g_signal_query (sigid, &query_info); + if (!pyg_gtype_is_custom (query_info.itype)) { + /* The signal is implemented by a non-Python class, probably + * something in the gi repository. */ + closure = pygi_signal_closure_new (self, query_info.itype, + query_info.signal_name, callback, + extra_args, object); + } + + if (!closure) { + /* The signal is either implemented at the Python level, or it comes + * from a foreign class that we don't have introspection data for. */ + closure = pyg_closure_new (callback, extra_args, object); + } + + pygobject_watch_closure((PyObject *)self, closure); + handlerid = g_signal_connect_closure_by_id(self->obj, sigid, detail, + closure, after); + return PyLong_FromUnsignedLong(handlerid); +} + +static PyObject * +pygobject_connect(PyGObject *self, PyObject *args) +{ + PyObject *first, *callback, *extra_args, *ret; + gchar *name; + guint len; + + len = PyTuple_Size(args); + if (len < 2) { + PyErr_SetString(PyExc_TypeError, + "GObject.connect requires at least 2 arguments"); + return NULL; + } + first = PySequence_GetSlice(args, 0, 2); + if (!PyArg_ParseTuple(first, "sO:GObject.connect", &name, &callback)) { + Py_DECREF(first); + return NULL; + } + Py_DECREF(first); + if (!PyCallable_Check(callback)) { + PyErr_SetString(PyExc_TypeError, "second argument must be callable"); + return NULL; + } + + CHECK_GOBJECT(self); + + extra_args = PySequence_GetSlice(args, 2, len); + if (extra_args == NULL) + return NULL; + + ret = connect_helper(self, name, callback, extra_args, NULL, FALSE); + Py_DECREF(extra_args); + return ret; +} + +static PyObject * +pygobject_connect_after(PyGObject *self, PyObject *args) +{ + PyObject *first, *callback, *extra_args, *ret; + gchar *name; + Py_ssize_t len; + + len = PyTuple_Size(args); + if (len < 2) { + PyErr_SetString(PyExc_TypeError, + "GObject.connect_after requires at least 2 arguments"); + return NULL; + } + first = PySequence_GetSlice(args, 0, 2); + if (!PyArg_ParseTuple(first, "sO:GObject.connect_after", + &name, &callback)) { + Py_DECREF(first); + return NULL; + } + Py_DECREF(first); + if (!PyCallable_Check(callback)) { + PyErr_SetString(PyExc_TypeError, "second argument must be callable"); + return NULL; + } + + CHECK_GOBJECT(self); + + extra_args = PySequence_GetSlice(args, 2, len); + if (extra_args == NULL) + return NULL; + + ret = connect_helper(self, name, callback, extra_args, NULL, TRUE); + Py_DECREF(extra_args); + return ret; +} + +static PyObject * +pygobject_connect_object(PyGObject *self, PyObject *args) +{ + PyObject *first, *callback, *extra_args, *object, *ret; + gchar *name; + Py_ssize_t len; + + len = PyTuple_Size(args); + if (len < 3) { + PyErr_SetString(PyExc_TypeError, + "GObject.connect_object requires at least 3 arguments"); + return NULL; + } + first = PySequence_GetSlice(args, 0, 3); + if (!PyArg_ParseTuple(first, "sOO:GObject.connect_object", + &name, &callback, &object)) { + Py_DECREF(first); + return NULL; + } + Py_DECREF(first); + if (!PyCallable_Check(callback)) { + PyErr_SetString(PyExc_TypeError, "second argument must be callable"); + return NULL; + } + + CHECK_GOBJECT(self); + + extra_args = PySequence_GetSlice(args, 3, len); + if (extra_args == NULL) + return NULL; + + ret = connect_helper(self, name, callback, extra_args, object, FALSE); + Py_DECREF(extra_args); + return ret; +} + +static PyObject * +pygobject_connect_object_after(PyGObject *self, PyObject *args) +{ + PyObject *first, *callback, *extra_args, *object, *ret; + gchar *name; + Py_ssize_t len; + + len = PyTuple_Size(args); + if (len < 3) { + PyErr_SetString(PyExc_TypeError, + "GObject.connect_object_after requires at least 3 arguments"); + return NULL; + } + first = PySequence_GetSlice(args, 0, 3); + if (!PyArg_ParseTuple(first, "sOO:GObject.connect_object_after", + &name, &callback, &object)) { + Py_DECREF(first); + return NULL; + } + Py_DECREF(first); + if (!PyCallable_Check(callback)) { + PyErr_SetString(PyExc_TypeError, "second argument must be callable"); + return NULL; + } + + CHECK_GOBJECT(self); + + extra_args = PySequence_GetSlice(args, 3, len); + if (extra_args == NULL) + return NULL; + + ret = connect_helper(self, name, callback, extra_args, object, TRUE); + Py_DECREF(extra_args); + return ret; +} + +static PyObject * +pygobject_emit(PyGObject *self, PyObject *args) +{ + guint signal_id, i, j; + Py_ssize_t len; + GQuark detail; + PyObject *first, *py_ret, *repr = NULL; + gchar *name; + GSignalQuery query; + GValue *params, ret = { 0, }; + + len = PyTuple_Size(args); + if (len < 1) { + PyErr_SetString(PyExc_TypeError,"GObject.emit needs at least one arg"); + return NULL; + } + first = PySequence_GetSlice(args, 0, 1); + if (!PyArg_ParseTuple(first, "s:GObject.emit", &name)) { + Py_DECREF(first); + return NULL; + } + Py_DECREF(first); + + CHECK_GOBJECT(self); + + if (!g_signal_parse_name(name, G_OBJECT_TYPE(self->obj), + &signal_id, &detail, TRUE)) { + repr = PyObject_Repr((PyObject*)self); + PyErr_Format(PyExc_TypeError, "%s: unknown signal name: %s", + PYGLIB_PyUnicode_AsString(repr), + name); + Py_DECREF(repr); + return NULL; + } + g_signal_query(signal_id, &query); + if (len != query.n_params + 1) { + gchar buf[128]; + + g_snprintf(buf, sizeof(buf), + "%d parameters needed for signal %s; %ld given", + query.n_params, name, (long int) (len - 1)); + PyErr_SetString(PyExc_TypeError, buf); + return NULL; + } + + params = g_new0(GValue, query.n_params + 1); + g_value_init(¶ms[0], G_OBJECT_TYPE(self->obj)); + g_value_set_object(¶ms[0], G_OBJECT(self->obj)); + + for (i = 0; i < query.n_params; i++) + g_value_init(¶ms[i + 1], + query.param_types[i] & ~G_SIGNAL_TYPE_STATIC_SCOPE); + for (i = 0; i < query.n_params; i++) { + PyObject *item = PyTuple_GetItem(args, i+1); + + if (pyg_value_from_pyobject(¶ms[i+1], item) < 0) { + gchar buf[128]; + g_snprintf(buf, sizeof(buf), + "could not convert type %s to %s required for parameter %d", + Py_TYPE(item)->tp_name, + G_VALUE_TYPE_NAME(¶ms[i+1]), i); + PyErr_SetString(PyExc_TypeError, buf); + + for (j = 0; j <= i; j++) + g_value_unset(¶ms[j]); + + g_free(params); + return NULL; + } + } + + if (query.return_type != G_TYPE_NONE) + g_value_init(&ret, query.return_type & ~G_SIGNAL_TYPE_STATIC_SCOPE); + + g_signal_emitv(params, signal_id, detail, &ret); + + for (i = 0; i < query.n_params + 1; i++) + g_value_unset(¶ms[i]); + + g_free(params); + if ((query.return_type & ~G_SIGNAL_TYPE_STATIC_SCOPE) != G_TYPE_NONE) { + py_ret = pyg_value_as_pyobject(&ret, TRUE); + g_value_unset(&ret); + } else { + Py_INCREF(Py_None); + py_ret = Py_None; + } + + return py_ret; +} + +static PyObject * +pygobject_chain_from_overridden(PyGObject *self, PyObject *args) +{ + GSignalInvocationHint *ihint; + guint signal_id, i; + Py_ssize_t len; + PyObject *py_ret; + const gchar *name; + GSignalQuery query; + GValue *params, ret = { 0, }; + + CHECK_GOBJECT(self); + + ihint = g_signal_get_invocation_hint(self->obj); + if (!ihint) { + PyErr_SetString(PyExc_TypeError, "could not find signal invocation " + "information for this object."); + return NULL; + } + + signal_id = ihint->signal_id; + name = g_signal_name(signal_id); + + len = PyTuple_Size(args); + if (signal_id == 0) { + PyErr_SetString(PyExc_TypeError, "unknown signal name"); + return NULL; + } + g_signal_query(signal_id, &query); + if (len != query.n_params) { + gchar buf[128]; + + g_snprintf(buf, sizeof(buf), + "%d parameters needed for signal %s; %ld given", + query.n_params, name, (long int) len); + PyErr_SetString(PyExc_TypeError, buf); + return NULL; + } + params = g_new0(GValue, query.n_params + 1); + g_value_init(¶ms[0], G_OBJECT_TYPE(self->obj)); + g_value_set_object(¶ms[0], G_OBJECT(self->obj)); + + for (i = 0; i < query.n_params; i++) + g_value_init(¶ms[i + 1], + query.param_types[i] & ~G_SIGNAL_TYPE_STATIC_SCOPE); + for (i = 0; i < query.n_params; i++) { + PyObject *item = PyTuple_GetItem(args, i); + + if (pyg_boxed_check(item, (query.param_types[i] & ~G_SIGNAL_TYPE_STATIC_SCOPE))) { + g_value_set_static_boxed(¶ms[i+1], pyg_boxed_get(item, void)); + } + else if (pyg_value_from_pyobject(¶ms[i+1], item) < 0) { + gchar buf[128]; + + g_snprintf(buf, sizeof(buf), + "could not convert type %s to %s required for parameter %d", + Py_TYPE(item)->tp_name, + g_type_name(G_VALUE_TYPE(¶ms[i+1])), i); + PyErr_SetString(PyExc_TypeError, buf); + for (i = 0; i < query.n_params + 1; i++) + g_value_unset(¶ms[i]); + g_free(params); + return NULL; + } + } + if (query.return_type != G_TYPE_NONE) + g_value_init(&ret, query.return_type & ~G_SIGNAL_TYPE_STATIC_SCOPE); + g_signal_chain_from_overridden(params, &ret); + for (i = 0; i < query.n_params + 1; i++) + g_value_unset(¶ms[i]); + g_free(params); + if (query.return_type != G_TYPE_NONE) { + py_ret = pyg_value_as_pyobject(&ret, TRUE); + g_value_unset(&ret); + } else { + Py_INCREF(Py_None); + py_ret = Py_None; + } + return py_ret; +} + + +static PyObject * +pygobject_weak_ref(PyGObject *self, PyObject *args) +{ + int len; + PyObject *callback = NULL, *user_data = NULL; + PyObject *retval; + + CHECK_GOBJECT(self); + + if ((len = PySequence_Length(args)) >= 1) { + callback = PySequence_ITEM(args, 0); + user_data = PySequence_GetSlice(args, 1, len); + } + retval = pygobject_weak_ref_new(self->obj, callback, user_data); + Py_XDECREF(callback); + Py_XDECREF(user_data); + return retval; +} + + +static PyObject * +pygobject_copy(PyGObject *self) +{ + PyErr_SetString(PyExc_TypeError, + "GObject descendants' instances are non-copyable"); + return NULL; +} + +static PyObject * +pygobject_deepcopy(PyGObject *self, PyObject *args) +{ + PyErr_SetString(PyExc_TypeError, + "GObject descendants' instances are non-copyable"); + return NULL; +} + + +static PyObject * +pygobject_disconnect_by_func(PyGObject *self, PyObject *args) +{ + PyObject *pyfunc = NULL, *repr = NULL; + GClosure *closure = NULL; + guint retval; + + CHECK_GOBJECT(self); + + if (!PyArg_ParseTuple(args, "O:GObject.disconnect_by_func", &pyfunc)) + return NULL; + + if (!PyCallable_Check(pyfunc)) { + PyErr_SetString(PyExc_TypeError, "first argument must be callable"); + return NULL; + } + + closure = gclosure_from_pyfunc(self, pyfunc); + if (!closure) { + repr = PyObject_Repr((PyObject*)pyfunc); + PyErr_Format(PyExc_TypeError, "nothing connected to %s", + PYGLIB_PyUnicode_AsString(repr)); + Py_DECREF(repr); + return NULL; + } + + retval = g_signal_handlers_disconnect_matched(self->obj, + G_SIGNAL_MATCH_CLOSURE, + 0, 0, + closure, + NULL, NULL); + return PYGLIB_PyLong_FromLong(retval); +} + +static PyObject * +pygobject_handler_block_by_func(PyGObject *self, PyObject *args) +{ + PyObject *pyfunc = NULL, *repr = NULL; + GClosure *closure = NULL; + guint retval; + + CHECK_GOBJECT(self); + + if (!PyArg_ParseTuple(args, "O:GObject.handler_block_by_func", &pyfunc)) + return NULL; + + if (!PyCallable_Check(pyfunc)) { + PyErr_SetString(PyExc_TypeError, "first argument must be callable"); + return NULL; + } + + closure = gclosure_from_pyfunc(self, pyfunc); + if (!closure) { + repr = PyObject_Repr((PyObject*)pyfunc); + PyErr_Format(PyExc_TypeError, "nothing connected to %s", + PYGLIB_PyUnicode_AsString(repr)); + Py_DECREF(repr); + return NULL; + } + + retval = g_signal_handlers_block_matched(self->obj, + G_SIGNAL_MATCH_CLOSURE, + 0, 0, + closure, + NULL, NULL); + return PYGLIB_PyLong_FromLong(retval); +} + +static PyObject * +pygobject_handler_unblock_by_func(PyGObject *self, PyObject *args) +{ + PyObject *pyfunc = NULL, *repr = NULL; + GClosure *closure = NULL; + guint retval; + + CHECK_GOBJECT(self); + + if (!PyArg_ParseTuple(args, "O:GObject.handler_unblock_by_func", &pyfunc)) + return NULL; + + if (!PyCallable_Check(pyfunc)) { + PyErr_SetString(PyExc_TypeError, "first argument must be callable"); + return NULL; + } + + closure = gclosure_from_pyfunc(self, pyfunc); + if (!closure) { + repr = PyObject_Repr((PyObject*)pyfunc); + PyErr_Format(PyExc_TypeError, "nothing connected to %s", + PYGLIB_PyUnicode_AsString(repr)); + Py_DECREF(repr); + return NULL; + } + + retval = g_signal_handlers_unblock_matched(self->obj, + G_SIGNAL_MATCH_CLOSURE, + 0, 0, + closure, + NULL, NULL); + return PYGLIB_PyLong_FromLong(retval); +} + + +static PyMethodDef pygobject_methods[] = { + { "get_property", (PyCFunction)pygobject_get_property, METH_VARARGS }, + { "get_properties", (PyCFunction)pygobject_get_properties, METH_VARARGS }, + { "set_property", (PyCFunction)pygobject_set_property, METH_VARARGS }, + { "set_properties", (PyCFunction)pygobject_set_properties, METH_VARARGS|METH_KEYWORDS }, + { "bind_property", (PyCFunction)pygobject_bind_property, METH_VARARGS|METH_KEYWORDS }, + { "connect", (PyCFunction)pygobject_connect, METH_VARARGS }, + { "connect_after", (PyCFunction)pygobject_connect_after, METH_VARARGS }, + { "connect_object", (PyCFunction)pygobject_connect_object, METH_VARARGS }, + { "connect_object_after", (PyCFunction)pygobject_connect_object_after, METH_VARARGS }, + { "disconnect_by_func", (PyCFunction)pygobject_disconnect_by_func, METH_VARARGS }, + { "handler_block_by_func", (PyCFunction)pygobject_handler_block_by_func, METH_VARARGS }, + { "handler_unblock_by_func", (PyCFunction)pygobject_handler_unblock_by_func, METH_VARARGS }, + { "emit", (PyCFunction)pygobject_emit, METH_VARARGS }, + { "chain", (PyCFunction)pygobject_chain_from_overridden,METH_VARARGS }, + { "weak_ref", (PyCFunction)pygobject_weak_ref, METH_VARARGS }, + { "__copy__", (PyCFunction)pygobject_copy, METH_NOARGS }, + { "__deepcopy__", (PyCFunction)pygobject_deepcopy, METH_VARARGS }, + { NULL, NULL, 0 } +}; + + +static PyObject * +pygobject_get_dict(PyGObject *self, void *closure) +{ + if (self->inst_dict == NULL) { + self->inst_dict = PyDict_New(); + if (self->inst_dict == NULL) + return NULL; + if (G_LIKELY(self->obj)) + pygobject_switch_to_toggle_ref(self); + } + Py_INCREF(self->inst_dict); + return self->inst_dict; +} + +static PyObject * +pygobject_get_refcount(PyGObject *self, void *closure) +{ + if (self->obj == NULL) { + PyErr_Format(PyExc_TypeError, "GObject instance is not yet created"); + return NULL; + } + return PYGLIB_PyLong_FromLong(self->obj->ref_count); +} + +static PyObject * +pygobject_get_pointer(PyGObject *self, void *closure) +{ + return PYGLIB_CPointer_WrapPointer (self->obj, NULL); +} + +static int +pygobject_setattro(PyObject *self, PyObject *name, PyObject *value) +{ + int res; + PyGObject *gself = (PyGObject *) self; + PyObject *inst_dict_before = gself->inst_dict; + /* call parent type's setattro */ + res = PyGObject_Type.tp_base->tp_setattro(self, name, value); + if (inst_dict_before == NULL && gself->inst_dict != NULL) { + if (G_LIKELY(gself->obj)) + pygobject_switch_to_toggle_ref(gself); + } + return res; +} + +static PyGetSetDef pygobject_getsets[] = { + { "__dict__", (getter)pygobject_get_dict, (setter)0 }, + { "__grefcount__", (getter)pygobject_get_refcount, (setter)0, }, + { "__gpointer__", (getter)pygobject_get_pointer, (setter)0, }, + { NULL, 0, 0 } +}; + +/* ------------------------------------ */ +/* ****** GObject weak reference ****** */ +/* ------------------------------------ */ + +typedef struct { + PyObject_HEAD + GObject *obj; + PyObject *callback; + PyObject *user_data; + gboolean have_floating_ref; +} PyGObjectWeakRef; + +PYGLIB_DEFINE_TYPE("gi._gobject.GObjectWeakRef", PyGObjectWeakRef_Type, PyGObjectWeakRef); + +static int +pygobject_weak_ref_traverse(PyGObjectWeakRef *self, visitproc visit, void *arg) +{ + if (self->callback && visit(self->callback, arg) < 0) + return -1; + if (self->user_data && visit(self->user_data, arg) < 0) + return -1; + return 0; +} + +static void +pygobject_weak_ref_notify(PyGObjectWeakRef *self, GObject *dummy) +{ + self->obj = NULL; + if (self->callback) { + PyObject *retval; + PyGILState_STATE state = pyglib_gil_state_ensure(); + retval = PyObject_Call(self->callback, self->user_data, NULL); + if (retval) { + if (retval != Py_None) + PyErr_Format(PyExc_TypeError, + "GObject weak notify callback returned a value" + " of type %s, should return None", + Py_TYPE(retval)->tp_name); + Py_DECREF(retval); + PyErr_Print(); + } else + PyErr_Print(); + Py_CLEAR(self->callback); + Py_CLEAR(self->user_data); + if (self->have_floating_ref) { + self->have_floating_ref = FALSE; + Py_DECREF((PyObject *) self); + } + pyglib_gil_state_release(state); + } +} + +static inline int +pygobject_weak_ref_clear(PyGObjectWeakRef *self) +{ + Py_CLEAR(self->callback); + Py_CLEAR(self->user_data); + if (self->obj) { + g_object_weak_unref(self->obj, (GWeakNotify) pygobject_weak_ref_notify, self); + self->obj = NULL; + } + return 0; +} + +static void +pygobject_weak_ref_dealloc(PyGObjectWeakRef *self) +{ + PyObject_GC_UnTrack((PyObject *)self); + pygobject_weak_ref_clear(self); + PyObject_GC_Del(self); +} + +static PyObject * +pygobject_weak_ref_new(GObject *obj, PyObject *callback, PyObject *user_data) +{ + PyGObjectWeakRef *self; + + self = PyObject_GC_New(PyGObjectWeakRef, &PyGObjectWeakRef_Type); + self->callback = callback; + self->user_data = user_data; + Py_XINCREF(self->callback); + Py_XINCREF(self->user_data); + self->obj = obj; + g_object_weak_ref(self->obj, (GWeakNotify) pygobject_weak_ref_notify, self); + if (callback != NULL) { + /* when we have a callback, we should INCREF the weakref + * object to make it stay alive even if it goes out of scope */ + self->have_floating_ref = TRUE; + Py_INCREF((PyObject *) self); + } + return (PyObject *) self; +} + +static PyObject * +pygobject_weak_ref_unref(PyGObjectWeakRef *self, PyObject *args) +{ + if (!self->obj) { + PyErr_SetString(PyExc_ValueError, "weak ref already unreffed"); + return NULL; + } + g_object_weak_unref(self->obj, (GWeakNotify) pygobject_weak_ref_notify, self); + self->obj = NULL; + if (self->have_floating_ref) { + self->have_floating_ref = FALSE; + Py_DECREF(self); + } + Py_INCREF(Py_None); + return Py_None; +} + +static PyMethodDef pygobject_weak_ref_methods[] = { + { "unref", (PyCFunction)pygobject_weak_ref_unref, METH_NOARGS}, + { NULL, NULL, 0} +}; + +static PyObject * +pygobject_weak_ref_call(PyGObjectWeakRef *self, PyObject *args, PyObject *kw) +{ + static char *argnames[] = {NULL}; + + if (!PyArg_ParseTupleAndKeywords(args, kw, ":__call__", argnames)) + return NULL; + + if (self->obj) + return pygobject_new(self->obj); + else { + Py_INCREF(Py_None); + return Py_None; + } +} + +static gpointer +pyobject_copy(gpointer boxed) +{ + PyObject *object = boxed; + PyGILState_STATE state; + + state = pyglib_gil_state_ensure(); + Py_INCREF(object); + pyglib_gil_state_release(state); + return object; +} + +static void +pyobject_free(gpointer boxed) +{ + PyObject *object = boxed; + PyGILState_STATE state; + + state = pyglib_gil_state_ensure(); + Py_DECREF(object); + pyglib_gil_state_release(state); +} + +void +pygobject_object_register_types(PyObject *d) +{ + PyObject *o, *descr; + + pygobject_custom_key = g_quark_from_static_string("PyGObject::custom"); + pygobject_class_key = g_quark_from_static_string("PyGObject::class"); + pygobject_class_init_key = g_quark_from_static_string("PyGObject::class-init"); + pygobject_wrapper_key = g_quark_from_static_string("PyGObject::wrapper"); + pygobject_has_updated_constructor_key = + g_quark_from_static_string("PyGObject::has-updated-constructor"); + pygobject_instance_data_key = g_quark_from_static_string("PyGObject::instance-data"); + + /* GObject */ + if (!PY_TYPE_OBJECT) + PY_TYPE_OBJECT = g_boxed_type_register_static("PyObject", + pyobject_copy, + pyobject_free); + PyGObject_Type.tp_dealloc = (destructor)pygobject_dealloc; + PyGObject_Type.tp_richcompare = pygobject_richcompare; + PyGObject_Type.tp_repr = (reprfunc)pygobject_repr; + PyGObject_Type.tp_hash = (hashfunc)pygobject_hash; + PyGObject_Type.tp_setattro = (setattrofunc)pygobject_setattro; + PyGObject_Type.tp_flags = (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | + Py_TPFLAGS_HAVE_GC); + PyGObject_Type.tp_traverse = (traverseproc)pygobject_traverse; + PyGObject_Type.tp_clear = (inquiry)pygobject_clear; + PyGObject_Type.tp_weaklistoffset = offsetof(PyGObject, weakreflist); + PyGObject_Type.tp_methods = pygobject_methods; + PyGObject_Type.tp_getset = pygobject_getsets; + PyGObject_Type.tp_dictoffset = offsetof(PyGObject, inst_dict); + PyGObject_Type.tp_init = (initproc)pygobject_init; + PyGObject_Type.tp_free = (freefunc)pygobject_free; + PyGObject_Type.tp_alloc = PyType_GenericAlloc; + PyGObject_Type.tp_new = PyType_GenericNew; + pygobject_register_class(d, "GObject", G_TYPE_OBJECT, + &PyGObject_Type, NULL); + PyDict_SetItemString(PyGObject_Type.tp_dict, "__gdoc__", + pyg_object_descr_doc_get()); + + /* GProps */ + PyGProps_Type.tp_dealloc = (destructor)PyGProps_dealloc; + PyGProps_Type.tp_as_sequence = (PySequenceMethods*)&_PyGProps_as_sequence; + PyGProps_Type.tp_getattro = (getattrofunc)PyGProps_getattro; + PyGProps_Type.tp_setattro = (setattrofunc)PyGProps_setattro; + PyGProps_Type.tp_flags = Py_TPFLAGS_DEFAULT|Py_TPFLAGS_HAVE_GC; + PyGProps_Type.tp_doc = "The properties of the GObject accessible as " + "Python attributes."; + PyGProps_Type.tp_traverse = (traverseproc)pygobject_props_traverse; + PyGProps_Type.tp_iter = (getiterfunc)pygobject_props_get_iter; + PyGProps_Type.tp_methods = pygobject_props_methods; + if (PyType_Ready(&PyGProps_Type) < 0) + return; + + /* GPropsDescr */ + PyGPropsDescr_Type.tp_flags = Py_TPFLAGS_DEFAULT; + PyGPropsDescr_Type.tp_descr_get = pyg_props_descr_descr_get; + if (PyType_Ready(&PyGPropsDescr_Type) < 0) + return; + descr = PyObject_New(PyObject, &PyGPropsDescr_Type); + PyDict_SetItemString(PyGObject_Type.tp_dict, "props", descr); + PyDict_SetItemString(PyGObject_Type.tp_dict, "__module__", + o=PYGLIB_PyUnicode_FromString("gi._gobject._gobject")); + Py_DECREF(o); + + /* GPropsIter */ + PyGPropsIter_Type.tp_dealloc = (destructor)pyg_props_iter_dealloc; + PyGPropsIter_Type.tp_flags = Py_TPFLAGS_DEFAULT; + PyGPropsIter_Type.tp_doc = "GObject properties iterator"; + PyGPropsIter_Type.tp_iternext = (iternextfunc)pygobject_props_iter_next; + if (PyType_Ready(&PyGPropsIter_Type) < 0) + return; + + PyGObjectWeakRef_Type.tp_dealloc = (destructor)pygobject_weak_ref_dealloc; + PyGObjectWeakRef_Type.tp_call = (ternaryfunc)pygobject_weak_ref_call; + PyGObjectWeakRef_Type.tp_flags = Py_TPFLAGS_DEFAULT|Py_TPFLAGS_HAVE_GC; + PyGObjectWeakRef_Type.tp_doc = "A GObject weak reference"; + PyGObjectWeakRef_Type.tp_traverse = (traverseproc)pygobject_weak_ref_traverse; + PyGObjectWeakRef_Type.tp_clear = (inquiry)pygobject_weak_ref_clear; + PyGObjectWeakRef_Type.tp_methods = pygobject_weak_ref_methods; + if (PyType_Ready(&PyGObjectWeakRef_Type) < 0) + return; + PyDict_SetItemString(d, "GObjectWeakRef", (PyObject *) &PyGObjectWeakRef_Type); +} diff --git a/gi/pygobject-object.h b/gi/pygobject-object.h new file mode 100644 index 00000000..fb39a259 --- /dev/null +++ b/gi/pygobject-object.h @@ -0,0 +1,56 @@ +#ifndef _PYGOBJECT_OBJECT_H_ +#define _PYGOBJECT_OBJECT_H_ + +#include +#include +#include "pyglib-python-compat.h" +#include "pygobject-internal.h" + +/* Data that belongs to the GObject instance, not the Python wrapper */ +struct _PyGObjectData { + PyTypeObject *type; /* wrapper type for this instance */ + GSList *closures; +}; + +extern GType PY_TYPE_OBJECT; +extern GQuark pygobject_instance_data_key; +extern GQuark pygobject_custom_key; +extern GQuark pygobject_wrapper_key; +extern GQuark pygobject_class_key; +extern GQuark pygobject_class_init_key; + +extern PyTypeObject PyGObjectWeakRef_Type; +extern PyTypeObject PyGPropsIter_Type; +extern PyTypeObject PyGPropsDescr_Type; +extern PyTypeObject PyGProps_Type; +extern PyTypeObject PyGObject_Type; +extern PyTypeObject *PyGObject_MetaType; + +static inline PyGObjectData * +pyg_object_peek_inst_data(GObject *obj) +{ + return ((PyGObjectData *) + g_object_get_qdata(obj, pygobject_instance_data_key)); +} + +gboolean pygobject_prepare_construct_properties (GObjectClass *class, + PyObject *kwargs, + guint *n_params, + GParameter **params); +void pygobject_register_class (PyObject *dict, + const gchar *type_name, + GType gtype, PyTypeObject *type, + PyObject *bases); +void pygobject_register_wrapper (PyObject *self); +PyObject * pygobject_new (GObject *obj); +PyObject * pygobject_new_full (GObject *obj, gboolean steal, gpointer g_class); +void pygobject_sink (GObject *obj); +PyTypeObject *pygobject_lookup_class (GType gtype); +void pygobject_watch_closure (PyObject *self, GClosure *closure); +void pygobject_object_register_types(PyObject *d); +void pygobject_ref_float(PyGObject *self); +void pygobject_ref_sink(PyGObject *self); + +GClosure * gclosure_from_pyfunc(PyGObject *object, PyObject *func); + +#endif /*_PYGOBJECT_OBJECT_H_*/ diff --git a/gi/pygobject-private.h b/gi/pygobject-private.h deleted file mode 100644 index b6242cd9..00000000 --- a/gi/pygobject-private.h +++ /dev/null @@ -1,186 +0,0 @@ -#ifndef _PYGOBJECT_PRIVATE_H_ -#define _PYGOBJECT_PRIVATE_H_ - -#ifdef _PYGOBJECT_H_ -# error "include pygobject.h or pygobject-private.h, but not both" -#endif - -#define _INSIDE_PYGOBJECT_ -#include "pygobject.h" - -#include "pyglib-python-compat.h" - -#define PYGOBJECT_REGISTER_GTYPE(d, type, name, gtype) \ - { \ - PyObject *o; \ - PYGLIB_REGISTER_TYPE(d, type, name); \ - PyDict_SetItemString(type.tp_dict, "__gtype__", \ - o=pyg_type_wrapper_new(gtype)); \ - Py_DECREF(o); \ -} - -/* from gobjectmodule.c */ -extern struct _PyGObject_Functions pygobject_api_functions; - - -#ifndef Py_CLEAR /* since Python 2.4 */ -# define Py_CLEAR(op) \ - do { \ - if (op) { \ - PyObject *tmp = (PyObject *)(op); \ - (op) = NULL; \ - Py_DECREF(tmp); \ - } \ - } while (0) -#endif - -extern GType PY_TYPE_OBJECT; - -extern GQuark pygboxed_type_key; -extern GQuark pygboxed_marshal_key; -extern GQuark pygenum_class_key; -extern GQuark pygflags_class_key; -extern GQuark pyginterface_type_key; -extern GQuark pyginterface_info_key; -extern GQuark pygobject_class_init_key; -extern GQuark pygobject_class_key; -extern GQuark pygobject_wrapper_key; -extern GQuark pygpointer_class_key; -extern GQuark pygobject_has_updated_constructor_key; -extern GQuark pygobject_instance_data_key; -extern GQuark pygobject_custom_key; - -void pygobject_data_free (PyGObjectData *data); -void pyg_destroy_notify (gpointer user_data); -gboolean pyg_handler_marshal (gpointer user_data); -int pygobject_constructv (PyGObject *self, - guint n_parameters, - GParameter *parameters); - -PyObject *pyg_integer_richcompare(PyObject *v, - PyObject *w, - int op); - -void pygobject_ref_float(PyGObject *self); -void pygobject_ref_sink(PyGObject *self); - -/* from pygtype.h */ -extern PyTypeObject PyGTypeWrapper_Type; - -PyObject *pyg_type_wrapper_new (GType type); -GType pyg_type_from_object_strict (PyObject *obj, gboolean strict); -GType pyg_type_from_object (PyObject *obj); - -gint pyg_enum_get_value (GType enum_type, PyObject *obj, gint *val); -gint pyg_flags_get_value (GType flag_type, PyObject *obj, guint *val); -int pyg_pyobj_to_unichar_conv (PyObject* py_obj, void* ptr); - -GClosure *pyg_closure_new(PyObject *callback, PyObject *extra_args, PyObject *swap_data); -void pyg_closure_set_exception_handler(GClosure *closure, - PyClosureExceptionHandler handler); -GClosure *pyg_signal_class_closure_get(void); -GClosure *gclosure_from_pyfunc(PyGObject *object, PyObject *func); - -PyObject *pyg_object_descr_doc_get(void); -void pygobject_object_register_types(PyObject *d); - -extern PyTypeObject *PyGObject_MetaType; - -/* from pygobject.h */ -extern PyTypeObject PyGObject_Type; -extern PyTypeObject PyGProps_Type; -extern PyTypeObject PyGPropsDescr_Type; -extern PyTypeObject PyGPropsIter_Type; - - /* Data that belongs to the GObject instance, not the Python wrapper */ -struct _PyGObjectData { - PyTypeObject *type; /* wrapper type for this instance */ - GSList *closures; -}; - -void pygobject_register_class (PyObject *dict, - const gchar *type_name, - GType gtype, PyTypeObject *type, - PyObject *bases); -void pygobject_register_wrapper (PyObject *self); -PyObject * pygobject_new (GObject *obj); -PyObject * pygobject_new_full (GObject *obj, gboolean steal, gpointer g_class); -void pygobject_sink (GObject *obj); -PyTypeObject *pygobject_lookup_class (GType gtype); -void pygobject_watch_closure (PyObject *self, GClosure *closure); -int pyg_type_register (PyTypeObject *class, - const gchar *type_name); - -/* from pygboxed.c */ -extern PyTypeObject PyGBoxed_Type; - -void pyg_register_boxed (PyObject *dict, const gchar *class_name, - GType boxed_type, PyTypeObject *type); -PyObject * pyg_boxed_new (GType boxed_type, gpointer boxed, - gboolean copy_boxed, gboolean own_ref); - -extern PyTypeObject PyGPointer_Type; - -void pyg_register_pointer (PyObject *dict, const gchar *class_name, - GType pointer_type, PyTypeObject *type); -PyObject * pyg_pointer_new (GType pointer_type, gpointer pointer); - -const gchar * pyg_constant_strip_prefix(const gchar *name, const gchar *strip_prefix); - -/* pygflags */ -typedef struct { - PYGLIB_PyLongObject parent; - int zero_pad; /* must always be 0 */ - GType gtype; -} PyGFlags; - -extern PyTypeObject PyGFlags_Type; - -#define PyGFlags_Check(x) (PyObject_IsInstance((PyObject *)x, (PyObject *)&PyGFlags_Type) && g_type_is_a(((PyGFlags*)x)->gtype, G_TYPE_FLAGS)) - -extern PyObject * pyg_flags_add (PyObject * module, - const char * type_name, - const char * strip_prefix, - GType gtype); -extern PyObject * pyg_flags_from_gtype (GType gtype, - guint value); - -/* pygenum */ -#define PyGEnum_Check(x) (PyObject_IsInstance((PyObject *)x, (PyObject *)&PyGEnum_Type) && g_type_is_a(((PyGFlags*)x)->gtype, G_TYPE_ENUM)) - -typedef struct { - PYGLIB_PyLongObject parent; - int zero_pad; /* must always be 0 */ - GType gtype; -} PyGEnum; - -extern PyTypeObject PyGEnum_Type; - -extern PyObject * pyg_enum_add (PyObject * module, - const char * type_name, - const char * strip_prefix, - GType gtype); -extern PyObject * pyg_enum_from_gtype (GType gtype, - int value); - -/* pygtype.c */ -extern gboolean pyg_gtype_is_custom (GType gtype); - -/* pygobject.c */ -extern PyTypeObject PyGObjectWeakRef_Type; - -static inline PyGObjectData * -pyg_object_peek_inst_data(GObject *obj) -{ - return ((PyGObjectData *) - g_object_get_qdata(obj, pygobject_instance_data_key)); -} - -gboolean pygobject_prepare_construct_properties (GObjectClass *class, - PyObject *kwargs, - guint *n_params, - GParameter **params); -/* Defined by PYGLIB_MODULE_START */ -extern PyObject *pyglib__gobject_module_create (void); - -#endif /*_PYGOBJECT_PRIVATE_H_*/ diff --git a/gi/pygobject.c b/gi/pygobject.c deleted file mode 100644 index e7ea5edc..00000000 --- a/gi/pygobject.c +++ /dev/null @@ -1,2406 +0,0 @@ -/* -*- Mode: C; c-basic-offset: 4 -*- - * pygtk- Python bindings for the GTK toolkit. - * Copyright (C) 1998-2003 James Henstridge - * - * pygobject.c: wrapper for the GObject type. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, see . - */ - -#ifdef HAVE_CONFIG_H -# include -#endif - -#include -#include "pygobject-private.h" -#include "pyginterface.h" -#include "pygparamspec.h" - -#include "pygi-value.h" -#include "pygi-type.h" -#include "pygi-property.h" -#include "pygi-signal-closure.h" - -extern PyObject *PyGIDeprecationWarning; - -static void pygobject_dealloc(PyGObject *self); -static int pygobject_traverse(PyGObject *self, visitproc visit, void *arg); -static int pygobject_clear(PyGObject *self); -static PyObject * pyg_type_get_bases(GType gtype); -static inline int pygobject_clear(PyGObject *self); -static PyObject * pygobject_weak_ref_new(GObject *obj, PyObject *callback, PyObject *user_data); -static inline PyGObjectData * pyg_object_peek_inst_data(GObject *obj); -static void pygobject_inherit_slots(PyTypeObject *type, PyObject *bases, - gboolean check_for_present); -static void pygobject_find_slot_for(PyTypeObject *type, PyObject *bases, int slot_offset, - gboolean check_for_present); -GType PY_TYPE_OBJECT = 0; -GQuark pygobject_custom_key; -GQuark pygobject_class_key; -GQuark pygobject_class_init_key; -GQuark pygobject_wrapper_key; -GQuark pygobject_has_updated_constructor_key; -GQuark pygobject_instance_data_key; - -/* Copied from glib. gobject uses hyphens in property names, but in Python - * we can only represent hyphens as underscores. Convert underscores to - * hyphens for glib compatibility. */ -static void -canonicalize_key (gchar *key) -{ - gchar *p; - - for (p = key; *p != 0; p++) - { - gchar c = *p; - - if (c != '-' && - (c < '0' || c > '9') && - (c < 'A' || c > 'Z') && - (c < 'a' || c > 'z')) - *p = '-'; - } -} - -/* -------------- class <-> wrapper manipulation --------------- */ - -void -pygobject_data_free(PyGObjectData *data) -{ - /* This function may be called after the python interpreter has already - * been shut down. If this happens, we cannot do any python calls, so just - * free the memory. */ - PyGILState_STATE state; - PyThreadState *_save = NULL; - gboolean state_saved = FALSE; - - GSList *closures, *tmp; - - if (Py_IsInitialized()) { - state_saved = TRUE; - state = pyglib_gil_state_ensure(); - Py_DECREF(data->type); - /* We cannot use Py_BEGIN_ALLOW_THREADS here because this is inside - * a branch. */ - Py_UNBLOCK_THREADS; /* Modifies _save */ - } - - tmp = closures = data->closures; -#ifndef NDEBUG - data->closures = NULL; - data->type = NULL; -#endif - while (tmp) { - GClosure *closure = tmp->data; - - /* we get next item first, because the current link gets - * invalidated by pygobject_unwatch_closure */ - tmp = tmp->next; - g_closure_invalidate(closure); - } - - if (data->closures != NULL) - g_warning("invalidated all closures, but data->closures != NULL !"); - - g_free(data); - - if (state_saved && Py_IsInitialized ()) { - Py_BLOCK_THREADS; /* Restores _save */ - pyglib_gil_state_release(state); - } -} - -static inline PyGObjectData * -pygobject_data_new(void) -{ - PyGObjectData *data; - data = g_new0(PyGObjectData, 1); - return data; -} - -static inline PyGObjectData * -pygobject_get_inst_data(PyGObject *self) -{ - PyGObjectData *inst_data; - - if (G_UNLIKELY(!self->obj)) - return NULL; - inst_data = g_object_get_qdata(self->obj, pygobject_instance_data_key); - if (inst_data == NULL) - { - inst_data = pygobject_data_new(); - - inst_data->type = Py_TYPE(self); - Py_INCREF((PyObject *) inst_data->type); - - g_object_set_qdata_full(self->obj, pygobject_instance_data_key, - inst_data, (GDestroyNotify) pygobject_data_free); - } - return inst_data; -} - - -PyTypeObject *PyGObject_MetaType = NULL; - -/** - * pygobject_sink: - * @obj: a GObject - * - * As Python handles reference counting for us, the "floating - * reference" code in GTK is not all that useful. In fact, it can - * cause leaks. This function should be called to remove the floating - * references on objects on construction. - **/ -void -pygobject_sink(GObject *obj) -{ - /* The default behaviour for GInitiallyUnowned subclasses is to call ref_sink(). - * - if the object is new and owned by someone else, its ref has been sunk and - * we need to keep the one from that someone and add our own "fresh ref" - * - if the object is not and owned by nobody, its ref is floating and we need - * to transform it into a regular ref. - */ - if (G_IS_INITIALLY_UNOWNED(obj)) { - g_object_ref_sink(obj); - } -} - -typedef struct { - PyObject_HEAD - GParamSpec **props; - guint n_props; - guint index; -} PyGPropsIter; - -PYGLIB_DEFINE_TYPE("gi._gobject.GPropsIter", PyGPropsIter_Type, PyGPropsIter); - -static void -pyg_props_iter_dealloc(PyGPropsIter *self) -{ - g_free(self->props); - PyObject_Del((PyObject*) self); -} - -static PyObject* -pygobject_props_iter_next(PyGPropsIter *iter) -{ - if (iter->index < iter->n_props) - return pyg_param_spec_new(iter->props[iter->index++]); - else { - PyErr_SetNone(PyExc_StopIteration); - return NULL; - } -} - -typedef struct { - PyObject_HEAD - /* a reference to the object containing the properties */ - PyGObject *pygobject; - GType gtype; -} PyGProps; - -static void -PyGProps_dealloc(PyGProps* self) -{ - PyGObject *tmp; - - PyObject_GC_UnTrack((PyObject*)self); - - tmp = self->pygobject; - self->pygobject = NULL; - Py_XDECREF(tmp); - - PyObject_GC_Del((PyObject*)self); -} - -static PyObject* -build_parameter_list(GObjectClass *class) -{ - GParamSpec **props; - guint n_props = 0, i; - PyObject *prop_str; - PyObject *props_list; - - props = g_object_class_list_properties(class, &n_props); - props_list = PyList_New(n_props); - for (i = 0; i < n_props; i++) { - char *name; - name = g_strdup(g_param_spec_get_name(props[i])); - /* hyphens cannot belong in identifiers */ - g_strdelimit(name, "-", '_'); - prop_str = PYGLIB_PyUnicode_FromString(name); - - PyList_SetItem(props_list, i, prop_str); - g_free(name); - } - - if (props) - g_free(props); - - return props_list; -} - -static PyObject* -PyGProps_getattro(PyGProps *self, PyObject *attr) -{ - char *attr_name, *property_name; - GObjectClass *class; - GParamSpec *pspec; - - attr_name = PYGLIB_PyUnicode_AsString(attr); - if (!attr_name) { - PyErr_Clear(); - return PyObject_GenericGetAttr((PyObject *)self, attr); - } - - class = g_type_class_ref(self->gtype); - - /* g_object_class_find_property recurses through the class hierarchy, - * so the resulting pspec tells us the owner_type that owns the property - * we're dealing with. */ - property_name = g_strdup(attr_name); - canonicalize_key(property_name); - pspec = g_object_class_find_property(class, property_name); - g_free(property_name); - g_type_class_unref(class); - - if (!pspec) { - return PyObject_GenericGetAttr((PyObject *)self, attr); - } - - if (!self->pygobject) { - /* If we're doing it without an instance, return a GParamSpec */ - return pyg_param_spec_new(pspec); - } - - return pygi_get_property_value (self->pygobject, pspec); -} - -static gboolean -set_property_from_pspec(GObject *obj, - GParamSpec *pspec, - PyObject *pvalue) -{ - GValue value = { 0, }; - - if (pspec->flags & G_PARAM_CONSTRUCT_ONLY) { - PyErr_Format(PyExc_TypeError, - "property '%s' can only be set in constructor", - pspec->name); - return FALSE; - } - - if (!(pspec->flags & G_PARAM_WRITABLE)) { - PyErr_Format(PyExc_TypeError, - "property '%s' is not writable", pspec->name); - return FALSE; - } - - g_value_init(&value, G_PARAM_SPEC_VALUE_TYPE(pspec)); - if (pyg_param_gvalue_from_pyobject(&value, pvalue, pspec) < 0) { - PyObject *pvalue_str = PyObject_Str(pvalue); - PyErr_Format(PyExc_TypeError, - "could not convert '%s' to type '%s' when setting property '%s.%s'", - PYGLIB_PyUnicode_AsString(pvalue_str), - g_type_name(G_PARAM_SPEC_VALUE_TYPE(pspec)), - G_OBJECT_TYPE_NAME(obj), - pspec->name); - Py_DECREF(pvalue_str); - return FALSE; - } - - Py_BEGIN_ALLOW_THREADS; - g_object_set_property(obj, pspec->name, &value); - g_value_unset(&value); - Py_END_ALLOW_THREADS; - - return TRUE; -} - -PYGLIB_DEFINE_TYPE("gi._gobject.GProps", PyGProps_Type, PyGProps); - -static int -PyGProps_setattro(PyGProps *self, PyObject *attr, PyObject *pvalue) -{ - GParamSpec *pspec; - char *attr_name, *property_name; - GObject *obj; - int ret = -1; - - if (pvalue == NULL) { - PyErr_SetString(PyExc_TypeError, "properties cannot be " - "deleted"); - return -1; - } - - attr_name = PYGLIB_PyUnicode_AsString(attr); - if (!attr_name) { - PyErr_Clear(); - return PyObject_GenericSetAttr((PyObject *)self, attr, pvalue); - } - - if (!self->pygobject) { - PyErr_SetString(PyExc_TypeError, - "cannot set GOject properties without an instance"); - return -1; - } - - obj = self->pygobject->obj; - - property_name = g_strdup(attr_name); - canonicalize_key(property_name); - - /* g_object_class_find_property recurses through the class hierarchy, - * so the resulting pspec tells us the owner_type that owns the property - * we're dealing with. */ - pspec = g_object_class_find_property(G_OBJECT_GET_CLASS(obj), - property_name); - g_free(property_name); - if (!pspec) { - return PyObject_GenericSetAttr((PyObject *)self, attr, pvalue); - } - if (!pyg_gtype_is_custom (pspec->owner_type)) { - /* This GType is not implemented in Python: see if we can set the - * property via gi. */ - ret = pygi_set_property_value (self->pygobject, pspec, pvalue); - if (ret == 0) - return 0; - else if (ret == -1 && PyErr_Occurred()) - return -1; - } - - /* This GType is implemented in Python, or we failed to set it via gi: - * do a straightforward set. */ - if (!set_property_from_pspec(obj, pspec, pvalue)) - return -1; - - return 0; -} - -static int -pygobject_props_traverse(PyGProps *self, visitproc visit, void *arg) -{ - if (self->pygobject && visit((PyObject *) self->pygobject, arg) < 0) - return -1; - return 0; -} - -static PyObject* -pygobject_props_get_iter(PyGProps *self) -{ - PyGPropsIter *iter; - GObjectClass *class; - - iter = PyObject_NEW(PyGPropsIter, &PyGPropsIter_Type); - class = g_type_class_ref(self->gtype); - iter->props = g_object_class_list_properties(class, &iter->n_props); - iter->index = 0; - g_type_class_unref(class); - return (PyObject *) iter; -} - -static PyObject* -pygobject_props_dir(PyGProps *self) -{ - PyObject *ret; - GObjectClass *class; - - class = g_type_class_ref (self->gtype); - ret = build_parameter_list (class); - g_type_class_unref (class); - - return ret; -} - -static PyMethodDef pygobject_props_methods[] = { - { "__dir__", (PyCFunction)pygobject_props_dir, METH_NOARGS}, - { NULL, NULL, 0} -}; - - -static Py_ssize_t -PyGProps_length(PyGProps *self) -{ - GObjectClass *class; - GParamSpec **props; - guint n_props; - - class = g_type_class_ref(self->gtype); - props = g_object_class_list_properties(class, &n_props); - g_type_class_unref(class); - g_free(props); - - return (Py_ssize_t)n_props; -} - -static PySequenceMethods _PyGProps_as_sequence = { - (lenfunc) PyGProps_length, - 0, - 0, - 0, - 0, - 0, - 0 -}; - -PYGLIB_DEFINE_TYPE("gi._gobject.GPropsDescr", PyGPropsDescr_Type, PyObject); - -static PyObject * -pyg_props_descr_descr_get(PyObject *self, PyObject *obj, PyObject *type) -{ - PyGProps *gprops; - - gprops = PyObject_GC_New(PyGProps, &PyGProps_Type); - if (obj == NULL || obj == Py_None) { - gprops->pygobject = NULL; - gprops->gtype = pyg_type_from_object(type); - } else { - if (!PyObject_IsInstance(obj, (PyObject *) &PyGObject_Type)) { - PyErr_SetString(PyExc_TypeError, "cannot use GObject property" - " descriptor on non-GObject instances"); - return NULL; - } - Py_INCREF(obj); - gprops->pygobject = (PyGObject *) obj; - gprops->gtype = pyg_type_from_object(obj); - } - return (PyObject *) gprops; -} - -/** - * pygobject_register_class: - * @dict: the module dictionary. A reference to the type will be stored here. - * @type_name: not used ? - * @gtype: the GType of the GObject subclass. - * @type: the Python type object for this wrapper. - * @static_bases: a tuple of Python type objects that are the bases of - * this type - * - * This function is used to register a Python type as the wrapper for - * a particular GObject subclass. It will also insert a reference to - * the wrapper class into the module dictionary passed as a reference, - * which simplifies initialisation. - */ -void -pygobject_register_class(PyObject *dict, const gchar *type_name, - GType gtype, PyTypeObject *type, - PyObject *static_bases) -{ - PyObject *o; - const char *class_name, *s; - PyObject *runtime_bases; - PyObject *bases_list, *bases, *mod_name; - int i; - - class_name = type->tp_name; - s = strrchr(class_name, '.'); - if (s != NULL) - class_name = s + 1; - - runtime_bases = pyg_type_get_bases(gtype); - if (static_bases) { - PyTypeObject *py_parent_type = (PyTypeObject *) PyTuple_GET_ITEM(static_bases, 0); - bases_list = PySequence_List(static_bases); - /* we start at index 1 because we want to skip the primary - * base, otherwise we might get MRO conflict */ - for (i = 1; i < PyTuple_GET_SIZE(runtime_bases); ++i) - { - PyObject *base = PyTuple_GET_ITEM(runtime_bases, i); - int contains = PySequence_Contains(bases_list, base); - if (contains < 0) - PyErr_Print(); - else if (!contains) { - if (!PySequence_Contains(py_parent_type->tp_mro, base)) { -#if 0 - g_message("Adding missing base %s to type %s", - ((PyTypeObject *)base)->tp_name, type->tp_name); -#endif - PyList_Append(bases_list, base); - } - } - } - bases = PySequence_Tuple(bases_list); - Py_DECREF(bases_list); - Py_DECREF(runtime_bases); - } else - bases = runtime_bases; - - Py_TYPE(type) = PyGObject_MetaType; - type->tp_bases = bases; - if (G_LIKELY(bases)) { - type->tp_base = (PyTypeObject *)PyTuple_GetItem(bases, 0); - Py_INCREF(type->tp_base); - } - - pygobject_inherit_slots(type, bases, TRUE); - - if (PyType_Ready(type) < 0) { - g_warning ("couldn't make the type `%s' ready", type->tp_name); - return; - } - - /* Set type.__module__ to the name of the module, - * otherwise it'll default to 'gobject', see #376099 - */ - s = strrchr(type->tp_name, '.'); - if (s != NULL) { - mod_name = PYGLIB_PyUnicode_FromStringAndSize(type->tp_name, (int)(s - type->tp_name)); - PyDict_SetItemString(type->tp_dict, "__module__", mod_name); - Py_DECREF(mod_name); - } - - if (gtype) { - o = pyg_type_wrapper_new(gtype); - PyDict_SetItemString(type->tp_dict, "__gtype__", o); - Py_DECREF(o); - - /* stash a pointer to the python class with the GType */ - Py_INCREF(type); - g_type_set_qdata(gtype, pygobject_class_key, type); - } - - /* set up __doc__ descriptor on type */ - PyDict_SetItemString(type->tp_dict, "__doc__", - pyg_object_descr_doc_get()); - - PyDict_SetItemString(dict, (char *)class_name, (PyObject *)type); -} - -static void -pyg_toggle_notify (gpointer data, GObject *object, gboolean is_last_ref) -{ - PyGObject *self; - PyGILState_STATE state; - - state = pyglib_gil_state_ensure(); - - /* Avoid thread safety problems by using qdata for wrapper retrieval - * instead of the user data argument. - * See: https://bugzilla.gnome.org/show_bug.cgi?id=709223 - */ - self = (PyGObject *)g_object_get_qdata (object, pygobject_wrapper_key); - if (self) { - if (is_last_ref) - Py_DECREF(self); - else - Py_INCREF(self); - } - - pyglib_gil_state_release(state); -} - - /* Called when the inst_dict is first created; switches the - reference counting strategy to start using toggle ref to keep the - wrapper alive while the GObject lives. In contrast, while - inst_dict was NULL the python wrapper is allowed to die at - will and is recreated on demand. */ -static inline void -pygobject_switch_to_toggle_ref(PyGObject *self) -{ - g_assert(self->obj->ref_count >= 1); - - if (self->private_flags.flags & PYGOBJECT_USING_TOGGLE_REF) - return; /* already using toggle ref */ - self->private_flags.flags |= PYGOBJECT_USING_TOGGLE_REF; - /* Note that add_toggle_ref will never immediately call back into - pyg_toggle_notify */ - Py_INCREF((PyObject *) self); - g_object_add_toggle_ref(self->obj, pyg_toggle_notify, NULL); - g_object_unref(self->obj); -} - -/* Called when an custom gobject is initalized via g_object_new instead of - its constructor. The next time the wrapper is access via - pygobject_new_full it will sink the floating reference instead of - adding a new reference and causing a leak */ - -void -pygobject_ref_float(PyGObject *self) -{ - /* should only be floated once */ - g_assert(!(self->private_flags.flags & PYGOBJECT_IS_FLOATING_REF)); - - self->private_flags.flags |= PYGOBJECT_IS_FLOATING_REF; -} - -/* Called by gobject_new_full, if the floating flag is set remove it, otherwise - ref the pyobject */ -void -pygobject_ref_sink(PyGObject *self) -{ - if (self->private_flags.flags & PYGOBJECT_IS_FLOATING_REF) - self->private_flags.flags &= ~PYGOBJECT_IS_FLOATING_REF; - else - Py_INCREF ( (PyObject *) self); -} - -/** - * pygobject_register_wrapper: - * @self: the wrapper instance - * - * In the constructor of PyGTK wrappers, this function should be - * called after setting the obj member. It will tie the wrapper - * instance to the GObject so that the same wrapper instance will - * always be used for this GObject instance. - */ -void -pygobject_register_wrapper(PyObject *self) -{ - PyGObject *gself; - - g_return_if_fail(self != NULL); - g_return_if_fail(PyObject_TypeCheck(self, &PyGObject_Type)); - - gself = (PyGObject *)self; - - g_assert(gself->obj->ref_count >= 1); - /* save wrapper pointer so we can access it later */ - g_object_set_qdata_full(gself->obj, pygobject_wrapper_key, gself, NULL); - if (gself->inst_dict) - pygobject_switch_to_toggle_ref(gself); -} - -static PyObject * -pyg_type_get_bases(GType gtype) -{ - GType *interfaces, parent_type, interface_type; - guint n_interfaces; - PyTypeObject *py_parent_type, *py_interface_type; - PyObject *bases; - int i; - - if (G_UNLIKELY(gtype == G_TYPE_OBJECT)) - return NULL; - - /* Lookup the parent type */ - parent_type = g_type_parent(gtype); - py_parent_type = pygobject_lookup_class(parent_type); - interfaces = g_type_interfaces(gtype, &n_interfaces); - bases = PyTuple_New(n_interfaces + 1); - /* We will always put the parent at the first position in bases */ - Py_INCREF(py_parent_type); /* PyTuple_SetItem steals a reference */ - PyTuple_SetItem(bases, 0, (PyObject *) py_parent_type); - - /* And traverse interfaces */ - if (n_interfaces) { - for (i = 0; i < n_interfaces; i++) { - interface_type = interfaces[i]; - py_interface_type = pygobject_lookup_class(interface_type); - Py_INCREF(py_interface_type); /* PyTuple_SetItem steals a reference */ - PyTuple_SetItem(bases, i + 1, (PyObject *) py_interface_type); - } - } - g_free(interfaces); - return bases; -} - -/** - * pygobject_new_with_interfaces - * @gtype: the GType of the GObject subclass. - * - * Creates a new PyTypeObject from the given GType with interfaces attached in - * bases. - * - * Returns: a PyTypeObject for the new type or NULL if it couldn't be created - */ -static PyTypeObject * -pygobject_new_with_interfaces(GType gtype) -{ - PyGILState_STATE state; - PyObject *o; - PyTypeObject *type; - PyObject *dict; - PyTypeObject *py_parent_type; - PyObject *bases; - - state = pyglib_gil_state_ensure(); - - bases = pyg_type_get_bases(gtype); - py_parent_type = (PyTypeObject *) PyTuple_GetItem(bases, 0); - - dict = PyDict_New(); - - o = pyg_type_wrapper_new(gtype); - PyDict_SetItemString(dict, "__gtype__", o); - Py_DECREF(o); - - /* set up __doc__ descriptor on type */ - PyDict_SetItemString(dict, "__doc__", pyg_object_descr_doc_get()); - - /* Something special to point out that it's not accessible through - * gi.repository */ - o = PYGLIB_PyUnicode_FromString ("__gi__"); - PyDict_SetItemString (dict, "__module__", o); - Py_DECREF (o); - - type = (PyTypeObject*)PyObject_CallFunction((PyObject *) Py_TYPE(py_parent_type), - "sNN", g_type_name (gtype), bases, dict); - - if (type == NULL) { - PyErr_Print(); - pyglib_gil_state_release(state); - return NULL; - } - - /* Workaround python tp_(get|set)attr slot inheritance bug. - * Fixes bug #144135. */ - if (!type->tp_getattr && py_parent_type->tp_getattr) { - type->tp_getattro = NULL; - type->tp_getattr = py_parent_type->tp_getattr; - } - if (!type->tp_setattr && py_parent_type->tp_setattr) { - type->tp_setattro = NULL; - type->tp_setattr = py_parent_type->tp_setattr; - } - /* override more python stupid hacks behind our back */ - type->tp_dealloc = py_parent_type->tp_dealloc; - type->tp_alloc = py_parent_type->tp_alloc; - type->tp_free = py_parent_type->tp_free; - type->tp_traverse = py_parent_type->tp_traverse; - type->tp_clear = py_parent_type->tp_clear; - - pygobject_inherit_slots(type, bases, FALSE); - - if (PyType_Ready(type) < 0) { - g_warning ("couldn't make the type `%s' ready", type->tp_name); - pyglib_gil_state_release(state); - return NULL; - } - - /* stash a pointer to the python class with the GType */ - Py_INCREF(type); - g_type_set_qdata(gtype, pygobject_class_key, type); - - pyglib_gil_state_release(state); - - return type; -} - -/* Pick appropriate value for given slot (at slot_offset inside - * PyTypeObject structure). It must be a pointer, e.g. a pointer to a - * function. We use the following heuristic: - * - * - Scan all types listed as bases of the type. - * - If for exactly one base type slot value is non-NULL and - * different from that of 'object' and 'GObject', set current type - * slot into that value. - * - Otherwise (if there is more than one such base type or none at - * all) don't touch it and live with Python default. - * - * The intention here is to propagate slot from custom wrappers to - * wrappers created at runtime when appropriate. We prefer to be on - * the safe side, so if there is potential collision (more than one - * custom slot value), we discard custom overrides altogether. - * - * When registering type with pygobject_register_class(), i.e. a type - * that has been manually created (likely with Codegen help), - * `check_for_present' should be set to TRUE. In this case, the - * function will never overwrite any non-NULL slots already present in - * the type. If `check_for_present' is FALSE, such non-NULL slots are - * though to be set by Python interpreter and so will be overwritten - * if heuristic above says so. - */ -static void -pygobject_inherit_slots(PyTypeObject *type, PyObject *bases, gboolean check_for_present) -{ - static int slot_offsets[] = { offsetof(PyTypeObject, tp_richcompare), -#if PY_VERSION_HEX < 0x03000000 - offsetof(PyTypeObject, tp_compare), -#endif - offsetof(PyTypeObject, tp_richcompare), - offsetof(PyTypeObject, tp_hash), - offsetof(PyTypeObject, tp_iter), - offsetof(PyTypeObject, tp_repr), - offsetof(PyTypeObject, tp_str), - offsetof(PyTypeObject, tp_print) }; - int i; - - /* Happens when registering gobject.GObject itself, at least. */ - if (!bases) - return; - - for (i = 0; i < G_N_ELEMENTS(slot_offsets); ++i) - pygobject_find_slot_for(type, bases, slot_offsets[i], check_for_present); -} - -static void -pygobject_find_slot_for(PyTypeObject *type, PyObject *bases, int slot_offset, - gboolean check_for_present) -{ -#define TYPE_SLOT(type) (* (void **) (((char *) (type)) + slot_offset)) - - void *found_slot = NULL; - int num_bases = PyTuple_Size(bases); - int i; - - if (check_for_present && TYPE_SLOT(type) != NULL) { - /* We are requested to check if there is any custom slot value - * in this type already and there actually is. Don't - * overwrite it. - */ - return; - } - - for (i = 0; i < num_bases; ++i) { - PyTypeObject *base_type = (PyTypeObject *) PyTuple_GetItem(bases, i); - void *slot = TYPE_SLOT(base_type); - - if (slot == NULL) - continue; - if (slot == TYPE_SLOT(&PyGObject_Type) || - slot == TYPE_SLOT(&PyBaseObject_Type)) - continue; - - if (found_slot != NULL && found_slot != slot) { - /* We have a conflict: more than one base use different - * custom slots. To be on the safe side, we bail out. - */ - return; - } - - found_slot = slot; - } - - /* Only perform the final assignment if at least one base has a - * custom value. Otherwise just leave this type's slot untouched. - */ - if (found_slot != NULL) - TYPE_SLOT(type) = found_slot; - -#undef TYPE_SLOT -} - -/** - * pygobject_lookup_class: - * @gtype: the GType of the GObject subclass. - * - * This function looks up the wrapper class used to represent - * instances of a GObject represented by @gtype. If no wrapper class - * or interface has been registered for the given GType, then a new - * type will be created. - * - * Returns: The wrapper class for the GObject or NULL if the - * GType has no registered type and a new type couldn't be created - */ -PyTypeObject * -pygobject_lookup_class(GType gtype) -{ - PyTypeObject *py_type; - - if (gtype == G_TYPE_INTERFACE) - return &PyGInterface_Type; - - py_type = g_type_get_qdata(gtype, pygobject_class_key); - if (py_type == NULL) { - py_type = g_type_get_qdata(gtype, pyginterface_type_key); - - if (py_type == NULL) - py_type = (PyTypeObject *)pygi_type_import_by_g_type(gtype); - - if (py_type == NULL) { - py_type = pygobject_new_with_interfaces(gtype); - g_type_set_qdata(gtype, pyginterface_type_key, py_type); - } - } - - return py_type; -} - -/** - * pygobject_new_full: - * @obj: a GObject instance. - * @steal: whether to steal a ref from the GObject or add (sink) a new one. - * @g_class: the GObjectClass - * - * This function gets a reference to a wrapper for the given GObject - * instance. If a wrapper has already been created, a new reference - * to that wrapper will be returned. Otherwise, a wrapper instance - * will be created. - * - * Returns: a reference to the wrapper for the GObject. - */ -PyObject * -pygobject_new_full(GObject *obj, gboolean steal, gpointer g_class) -{ - PyGObject *self; - - if (obj == NULL) { - Py_RETURN_NONE; - } - - /* If the GObject already has a PyObject wrapper stashed in its qdata, re-use it. - */ - self = (PyGObject *)g_object_get_qdata(obj, pygobject_wrapper_key); - if (self != NULL) { - /* Note the use of "pygobject_ref_sink" here only deals with PyObject - * wrapper ref counts and has nothing to do with GObject. - */ - pygobject_ref_sink(self); - - /* If steal is true, we also want to decref the incoming GObjects which - * already have a Python wrapper because the wrapper is already holding a - * strong reference. - */ - if (steal) - g_object_unref (obj); - - } else { - /* create wrapper */ - PyGObjectData *inst_data = pyg_object_peek_inst_data(obj); - PyTypeObject *tp; - if (inst_data) - tp = inst_data->type; - else { - if (g_class) - tp = pygobject_lookup_class(G_OBJECT_CLASS_TYPE(g_class)); - else - tp = pygobject_lookup_class(G_OBJECT_TYPE(obj)); - } - g_assert(tp != NULL); - - /* need to bump type refcount if created with - pygobject_new_with_interfaces(). fixes bug #141042 */ - if (tp->tp_flags & Py_TPFLAGS_HEAPTYPE) - Py_INCREF(tp); - self = PyObject_GC_New(PyGObject, tp); - if (self == NULL) - return NULL; - self->inst_dict = NULL; - self->weakreflist = NULL; - self->private_flags.flags = 0; - self->obj = obj; - - /* If we are not stealing a ref or the object is floating, - * add a regular ref or sink the object. */ - if (g_object_is_floating (obj)) - self->private_flags.flags |= PYGOBJECT_GOBJECT_WAS_FLOATING; - if (!steal || self->private_flags.flags & PYGOBJECT_GOBJECT_WAS_FLOATING) - g_object_ref_sink (obj); - - pygobject_register_wrapper((PyObject *)self); - PyObject_GC_Track((PyObject *)self); - } - - return (PyObject *)self; -} - - -PyObject * -pygobject_new(GObject *obj) -{ - return pygobject_new_full(obj, - /*steal=*/FALSE, - NULL); -} - -static void -pygobject_unwatch_closure(gpointer data, GClosure *closure) -{ - PyGObjectData *inst_data = data; - - inst_data->closures = g_slist_remove (inst_data->closures, closure); -} - -/** - * pygobject_watch_closure: - * @self: a GObject wrapper instance - * @closure: a GClosure to watch - * - * Adds a closure to the list of watched closures for the wrapper. - * The closure must be one returned by pyg_closure_new(). When the - * cycle GC traverses the wrapper instance, it will enumerate the - * references to Python objects stored in watched closures. If the - * cycle GC tells the wrapper to clear itself, the watched closures - * will be invalidated. - */ -void -pygobject_watch_closure(PyObject *self, GClosure *closure) -{ - PyGObject *gself; - PyGObjectData *data; - - g_return_if_fail(self != NULL); - g_return_if_fail(PyObject_TypeCheck(self, &PyGObject_Type)); - g_return_if_fail(closure != NULL); - - gself = (PyGObject *)self; - data = pygobject_get_inst_data(gself); - g_return_if_fail(g_slist_find(data->closures, closure) == NULL); - data->closures = g_slist_prepend(data->closures, closure); - g_closure_add_invalidate_notifier(closure, data, pygobject_unwatch_closure); -} - - -/* -------------- PyGObject behaviour ----------------- */ - -PYGLIB_DEFINE_TYPE("gi._gobject.GObject", PyGObject_Type, PyGObject); - -static void -pygobject_dealloc(PyGObject *self) -{ - /* Untrack must be done first. This is because followup calls such as - * ClearWeakRefs could call into Python and cause new allocations to - * happen, which could in turn could trigger the garbage collector, - * which would then get confused as it is tracking this half-deallocated - * object. */ - PyObject_GC_UnTrack((PyObject *)self); - - PyObject_ClearWeakRefs((PyObject *)self); - /* this forces inst_data->type to be updated, which could prove - * important if a new wrapper has to be created and it is of a - * unregistered type */ - pygobject_get_inst_data(self); - pygobject_clear(self); - /* the following causes problems with subclassed types */ - /* Py_TYPE(self)->tp_free((PyObject *)self); */ - PyObject_GC_Del(self); -} - -static PyObject* -pygobject_richcompare(PyObject *self, PyObject *other, int op) -{ - int isinst; - - isinst = PyObject_IsInstance(self, (PyObject*)&PyGObject_Type); - if (isinst == -1) - return NULL; - if (!isinst) { - Py_INCREF(Py_NotImplemented); - return Py_NotImplemented; - } - isinst = PyObject_IsInstance(other, (PyObject*)&PyGObject_Type); - if (isinst == -1) - return NULL; - if (!isinst) { - Py_INCREF(Py_NotImplemented); - return Py_NotImplemented; - } - - return _pyglib_generic_ptr_richcompare(((PyGObject*)self)->obj, - ((PyGObject*)other)->obj, - op); -} - -static long -pygobject_hash(PyGObject *self) -{ - return (long)self->obj; -} - -static PyObject * -pygobject_repr(PyGObject *self) -{ - PyObject *module, *repr; - gchar *module_str, *namespace; - - module = PyObject_GetAttrString ((PyObject *)self, "__module__"); - if (module == NULL) - return NULL; - - if (!PYGLIB_PyUnicode_Check (module)) { - Py_DECREF (module); - return NULL; - } - - module_str = PYGLIB_PyUnicode_AsString (module); - namespace = g_strrstr (module_str, "."); - if (namespace == NULL) { - namespace = module_str; - } else { - namespace += 1; - } - - repr = PYGLIB_PyUnicode_FromFormat ("<%s.%s object at %p (%s at %p)>", - namespace, Py_TYPE (self)->tp_name, self, - self->obj ? G_OBJECT_TYPE_NAME (self->obj) : "uninitialized", - self->obj); - Py_DECREF (module); - return repr; -} - - -static int -pygobject_traverse(PyGObject *self, visitproc visit, void *arg) -{ - int ret = 0; - GSList *tmp; - PyGObjectData *data = pygobject_get_inst_data(self); - - if (self->inst_dict) ret = visit(self->inst_dict, arg); - if (ret != 0) return ret; - - if (data) { - - for (tmp = data->closures; tmp != NULL; tmp = tmp->next) { - PyGClosure *closure = tmp->data; - - if (closure->callback) ret = visit(closure->callback, arg); - if (ret != 0) return ret; - - if (closure->extra_args) ret = visit(closure->extra_args, arg); - if (ret != 0) return ret; - - if (closure->swap_data) ret = visit(closure->swap_data, arg); - if (ret != 0) return ret; - } - } - return ret; -} - -static inline int -pygobject_clear(PyGObject *self) -{ - if (self->obj) { - g_object_set_qdata_full(self->obj, pygobject_wrapper_key, NULL, NULL); - if (self->inst_dict) { - g_object_remove_toggle_ref(self->obj, pyg_toggle_notify, NULL); - self->private_flags.flags &= ~PYGOBJECT_USING_TOGGLE_REF; - } else { - Py_BEGIN_ALLOW_THREADS; - g_object_unref(self->obj); - Py_END_ALLOW_THREADS; - } - self->obj = NULL; - } - Py_CLEAR(self->inst_dict); - return 0; -} - -static void -pygobject_free(PyObject *op) -{ - PyObject_GC_Del(op); -} - -gboolean -pygobject_prepare_construct_properties(GObjectClass *class, PyObject *kwargs, - guint *n_params, GParameter **params) -{ - *n_params = 0; - *params = NULL; - - if (kwargs) { - Py_ssize_t pos = 0; - PyObject *key; - PyObject *value; - - *params = g_new0(GParameter, PyDict_Size(kwargs)); - while (PyDict_Next(kwargs, &pos, &key, &value)) { - GParamSpec *pspec; - GParameter *param = &(*params)[*n_params]; - const gchar *key_str = PYGLIB_PyUnicode_AsString(key); - - pspec = g_object_class_find_property(class, key_str); - if (!pspec) { - PyErr_Format(PyExc_TypeError, - "gobject `%s' doesn't support property `%s'", - G_OBJECT_CLASS_NAME(class), key_str); - return FALSE; - } - g_value_init(¶m->value, G_PARAM_SPEC_VALUE_TYPE(pspec)); - if (pyg_param_gvalue_from_pyobject(¶m->value, value, pspec) < 0) { - PyErr_Format(PyExc_TypeError, - "could not convert value for property `%s' from %s to %s", - key_str, Py_TYPE(value)->tp_name, - g_type_name(G_PARAM_SPEC_VALUE_TYPE(pspec))); - return FALSE; - } - param->name = g_strdup(key_str); - ++(*n_params); - } - } - return TRUE; -} - -/* ---------------- PyGObject methods ----------------- */ - -static int -pygobject_init(PyGObject *self, PyObject *args, PyObject *kwargs) -{ - GType object_type; - guint n_params = 0, i; - GParameter *params = NULL; - GObjectClass *class; - - /* Only do GObject creation and property setting if the GObject hasn't - * already been created. The case where self->obj already exists can occur - * when C constructors are called directly (Gtk.Button.new_with_label) - * and we are simply wrapping the result with a PyGObject. - * In these cases we want to ignore any keyword arguments passed along - * to __init__ and simply return. - * - * See: https://bugzilla.gnome.org/show_bug.cgi?id=705810 - */ - if (self->obj != NULL) - return 0; - - if (!PyArg_ParseTuple(args, ":GObject.__init__", NULL)) - return -1; - - object_type = pyg_type_from_object((PyObject *)self); - if (!object_type) - return -1; - - if (G_TYPE_IS_ABSTRACT(object_type)) { - PyErr_Format(PyExc_TypeError, "cannot create instance of abstract " - "(non-instantiable) type `%s'", g_type_name(object_type)); - return -1; - } - - if ((class = g_type_class_ref (object_type)) == NULL) { - PyErr_SetString(PyExc_TypeError, - "could not get a reference to type class"); - return -1; - } - - if (!pygobject_prepare_construct_properties (class, kwargs, &n_params, ¶ms)) - goto cleanup; - - if (pygobject_constructv(self, n_params, params)) - PyErr_SetString(PyExc_RuntimeError, "could not create object"); - - cleanup: - for (i = 0; i < n_params; i++) { - g_free((gchar *) params[i].name); - g_value_unset(¶ms[i].value); - } - g_free(params); - g_type_class_unref(class); - - return (self->obj) ? 0 : -1; -} - -#define CHECK_GOBJECT(self) \ - if (!G_IS_OBJECT(self->obj)) { \ - PyErr_Format(PyExc_TypeError, \ - "object at %p of type %s is not initialized", \ - self, Py_TYPE(self)->tp_name); \ - return NULL; \ - } - -static PyObject * -pygobject_get_property (PyGObject *self, PyObject *args) -{ - gchar *param_name; - - if (!PyArg_ParseTuple (args, "s:GObject.get_property", ¶m_name)) { - return NULL; - } - - CHECK_GOBJECT(self); - - return pygi_get_property_value_by_name (self, param_name); -} - -static PyObject * -pygobject_get_properties(PyGObject *self, PyObject *args) -{ - int len, i; - PyObject *tuple; - - if ((len = PyTuple_Size(args)) < 1) { - PyErr_SetString(PyExc_TypeError, "requires at least one argument"); - return NULL; - } - - tuple = PyTuple_New(len); - for (i = 0; i < len; i++) { - PyObject *py_property = PyTuple_GetItem(args, i); - gchar *property_name; - PyObject *item; - - if (!PYGLIB_PyUnicode_Check(py_property)) { - PyErr_SetString(PyExc_TypeError, - "Expected string argument for property."); - goto fail; - } - - property_name = PYGLIB_PyUnicode_AsString(py_property); - item = pygi_get_property_value_by_name (self, property_name); - PyTuple_SetItem (tuple, i, item); - } - - return tuple; - -fail: - Py_DECREF (tuple); - return NULL; -} - -static PyObject * -pygobject_set_property(PyGObject *self, PyObject *args) -{ - gchar *param_name; - GParamSpec *pspec; - PyObject *pvalue; - int ret = -1; - - if (!PyArg_ParseTuple(args, "sO:GObject.set_property", ¶m_name, - &pvalue)) - return NULL; - - CHECK_GOBJECT(self); - - pspec = g_object_class_find_property(G_OBJECT_GET_CLASS(self->obj), - param_name); - if (!pspec) { - PyErr_Format(PyExc_TypeError, - "object of type `%s' does not have property `%s'", - g_type_name(G_OBJECT_TYPE(self->obj)), param_name); - return NULL; - } - - ret = pygi_set_property_value (self, pspec, pvalue); - if (ret == 0) - goto done; - else if (PyErr_Occurred()) - return NULL; - - if (!set_property_from_pspec(self->obj, pspec, pvalue)) - return NULL; - -done: - - Py_INCREF(Py_None); - return Py_None; -} - -static PyObject * -pygobject_set_properties(PyGObject *self, PyObject *args, PyObject *kwargs) -{ - GObjectClass *class; - Py_ssize_t pos; - PyObject *value; - PyObject *key; - PyObject *result = NULL; - - CHECK_GOBJECT(self); - - class = G_OBJECT_GET_CLASS(self->obj); - - g_object_freeze_notify (G_OBJECT(self->obj)); - pos = 0; - - while (kwargs && PyDict_Next (kwargs, &pos, &key, &value)) { - gchar *key_str = PYGLIB_PyUnicode_AsString(key); - GParamSpec *pspec; - int ret = -1; - - pspec = g_object_class_find_property(class, key_str); - if (!pspec) { - gchar buf[512]; - - g_snprintf(buf, sizeof(buf), - "object `%s' doesn't support property `%s'", - g_type_name(G_OBJECT_TYPE(self->obj)), key_str); - PyErr_SetString(PyExc_TypeError, buf); - goto exit; - } - - ret = pygi_set_property_value (self, pspec, value); - if (ret != 0) { - /* Non-zero return code means that either an error occured ...*/ - if (PyErr_Occurred()) - goto exit; - - /* ... or the property couldn't be found , so let's try the default - * call. */ - if (!set_property_from_pspec(G_OBJECT(self->obj), pspec, value)) - goto exit; - } - } - - result = Py_None; - - exit: - g_object_thaw_notify (G_OBJECT(self->obj)); - Py_XINCREF(result); - return result; -} - -/* custom closure for gobject bindings */ -static void -pygbinding_closure_invalidate(gpointer data, GClosure *closure) -{ - PyGClosure *pc = (PyGClosure *)closure; - PyGILState_STATE state; - - state = pyglib_gil_state_ensure(); - Py_XDECREF(pc->callback); - Py_XDECREF(pc->extra_args); - pyglib_gil_state_release(state); - - pc->callback = NULL; - pc->extra_args = NULL; -} - -static void -pygbinding_marshal (GClosure *closure, - GValue *return_value, - guint n_param_values, - const GValue *param_values, - gpointer invocation_hint, - gpointer marshal_data) -{ - PyGILState_STATE state; - PyGClosure *pc = (PyGClosure *)closure; - PyObject *params, *ret; - GValue *out_value; - - state = pyglib_gil_state_ensure(); - - /* construct Python tuple for the parameter values */ - params = PyTuple_New(2); - PyTuple_SetItem (params, 0, pyg_value_as_pyobject(¶m_values[0], FALSE)); - PyTuple_SetItem (params, 1, pyg_value_as_pyobject(¶m_values[1], FALSE)); - - /* params passed to function may have extra arguments */ - if (pc->extra_args) { - PyObject *tuple = params; - params = PySequence_Concat(tuple, pc->extra_args); - Py_DECREF(tuple); - } - ret = PyObject_CallObject(pc->callback, params); - if (!ret) { - PyErr_Print (); - goto out; - } else if (ret == Py_None) { - g_value_set_boolean (return_value, FALSE); - goto out; - } - - out_value = g_value_get_boxed (¶m_values[2]); - if (pyg_value_from_pyobject (out_value, ret) != 0) { - PyErr_SetString (PyExc_ValueError, "can't convert value"); - PyErr_Print (); - g_value_set_boolean (return_value, FALSE); - } else { - g_value_set_boolean (return_value, TRUE); - } - - Py_DECREF(ret); - -out: - Py_DECREF(params); - pyglib_gil_state_release(state); -} - -static GClosure * -pygbinding_closure_new (PyObject *callback, PyObject *extra_args) -{ - GClosure *closure; - - g_return_val_if_fail(callback != NULL, NULL); - closure = g_closure_new_simple(sizeof(PyGClosure), NULL); - g_closure_add_invalidate_notifier(closure, NULL, pygbinding_closure_invalidate); - g_closure_set_marshal(closure, pygbinding_marshal); - Py_INCREF(callback); - ((PyGClosure *)closure)->callback = callback; - if (extra_args && extra_args != Py_None) { - Py_INCREF(extra_args); - if (!PyTuple_Check(extra_args)) { - PyObject *tmp = PyTuple_New(1); - PyTuple_SetItem(tmp, 0, extra_args); - extra_args = tmp; - } - ((PyGClosure *)closure)->extra_args = extra_args; - } - return closure; -} - -static PyObject * -pygobject_bind_property(PyGObject *self, PyObject *args) -{ - gchar *source_name, *target_name; - gchar *source_canon, *target_canon; - PyObject *target, *source_repr, *target_repr; - PyObject *transform_to, *transform_from, *user_data = NULL; - GBinding *binding; - GBindingFlags flags = G_BINDING_DEFAULT; - GClosure *to_closure = NULL, *from_closure = NULL; - - transform_from = NULL; - transform_to = NULL; - - if (!PyArg_ParseTuple(args, "sOs|iOOO:GObject.bind_property", - &source_name, &target, &target_name, &flags, - &transform_to, &transform_from, &user_data)) - return NULL; - - CHECK_GOBJECT(self); - if (!PyObject_TypeCheck(target, &PyGObject_Type)) { - PyErr_SetString(PyExc_TypeError, "Second argument must be a GObject"); - return NULL; - } - - if (transform_to && transform_to != Py_None) { - if (!PyCallable_Check (transform_to)) { - PyErr_SetString (PyExc_TypeError, - "transform_to must be callable or None"); - return NULL; - } - to_closure = pygbinding_closure_new (transform_to, user_data); - } - - if (transform_from && transform_from != Py_None) { - if (!PyCallable_Check (transform_from)) { - PyErr_SetString (PyExc_TypeError, - "transform_from must be callable or None"); - return NULL; - } - from_closure = pygbinding_closure_new (transform_from, user_data); - } - - /* Canonicalize underscores to hyphens. Note the results must be freed. */ - source_canon = g_strdelimit(g_strdup(source_name), "_", '-'); - target_canon = g_strdelimit(g_strdup(target_name), "_", '-'); - - binding = g_object_bind_property_with_closures (G_OBJECT(self->obj), source_canon, - pygobject_get(target), target_canon, - flags, to_closure, from_closure); - g_free(source_canon); - g_free(target_canon); - source_canon = target_canon = NULL; - - if (binding == NULL) { - source_repr = PyObject_Repr((PyObject*)self); - target_repr = PyObject_Repr(target); - PyErr_Format(PyExc_TypeError, "Cannot create binding from %s.%s to %s.%s", - PYGLIB_PyUnicode_AsString(source_repr), source_name, - PYGLIB_PyUnicode_AsString(target_repr), target_name); - Py_DECREF(source_repr); - Py_DECREF(target_repr); - return NULL; - } - - return pygobject_new (G_OBJECT (binding)); -} - -static PyObject * -connect_helper(PyGObject *self, gchar *name, PyObject *callback, PyObject *extra_args, PyObject *object, gboolean after) -{ - guint sigid; - GQuark detail = 0; - GClosure *closure = NULL; - gulong handlerid; - GSignalQuery query_info; - - if (!g_signal_parse_name(name, G_OBJECT_TYPE(self->obj), - &sigid, &detail, TRUE)) { - PyObject *repr = PyObject_Repr((PyObject*)self); - PyErr_Format(PyExc_TypeError, "%s: unknown signal name: %s", - PYGLIB_PyUnicode_AsString(repr), - name); - Py_DECREF(repr); - return NULL; - } - - if (object && !PyObject_TypeCheck (object, &PyGObject_Type)) { - if (PyErr_WarnEx (PyGIDeprecationWarning, - "Using non GObject arguments for connect_object() is deprecated, use: " - "connect_data(signal, callback, data, connect_flags=GObject.ConnectFlags.SWAPPED)", - 1)) { - return NULL; - } - } - - g_signal_query (sigid, &query_info); - if (!pyg_gtype_is_custom (query_info.itype)) { - /* The signal is implemented by a non-Python class, probably - * something in the gi repository. */ - closure = pygi_signal_closure_new (self, query_info.itype, - query_info.signal_name, callback, - extra_args, object); - } - - if (!closure) { - /* The signal is either implemented at the Python level, or it comes - * from a foreign class that we don't have introspection data for. */ - closure = pyg_closure_new (callback, extra_args, object); - } - - pygobject_watch_closure((PyObject *)self, closure); - handlerid = g_signal_connect_closure_by_id(self->obj, sigid, detail, - closure, after); - return PyLong_FromUnsignedLong(handlerid); -} - -static PyObject * -pygobject_connect(PyGObject *self, PyObject *args) -{ - PyObject *first, *callback, *extra_args, *ret; - gchar *name; - guint len; - - len = PyTuple_Size(args); - if (len < 2) { - PyErr_SetString(PyExc_TypeError, - "GObject.connect requires at least 2 arguments"); - return NULL; - } - first = PySequence_GetSlice(args, 0, 2); - if (!PyArg_ParseTuple(first, "sO:GObject.connect", &name, &callback)) { - Py_DECREF(first); - return NULL; - } - Py_DECREF(first); - if (!PyCallable_Check(callback)) { - PyErr_SetString(PyExc_TypeError, "second argument must be callable"); - return NULL; - } - - CHECK_GOBJECT(self); - - extra_args = PySequence_GetSlice(args, 2, len); - if (extra_args == NULL) - return NULL; - - ret = connect_helper(self, name, callback, extra_args, NULL, FALSE); - Py_DECREF(extra_args); - return ret; -} - -static PyObject * -pygobject_connect_after(PyGObject *self, PyObject *args) -{ - PyObject *first, *callback, *extra_args, *ret; - gchar *name; - Py_ssize_t len; - - len = PyTuple_Size(args); - if (len < 2) { - PyErr_SetString(PyExc_TypeError, - "GObject.connect_after requires at least 2 arguments"); - return NULL; - } - first = PySequence_GetSlice(args, 0, 2); - if (!PyArg_ParseTuple(first, "sO:GObject.connect_after", - &name, &callback)) { - Py_DECREF(first); - return NULL; - } - Py_DECREF(first); - if (!PyCallable_Check(callback)) { - PyErr_SetString(PyExc_TypeError, "second argument must be callable"); - return NULL; - } - - CHECK_GOBJECT(self); - - extra_args = PySequence_GetSlice(args, 2, len); - if (extra_args == NULL) - return NULL; - - ret = connect_helper(self, name, callback, extra_args, NULL, TRUE); - Py_DECREF(extra_args); - return ret; -} - -static PyObject * -pygobject_connect_object(PyGObject *self, PyObject *args) -{ - PyObject *first, *callback, *extra_args, *object, *ret; - gchar *name; - Py_ssize_t len; - - len = PyTuple_Size(args); - if (len < 3) { - PyErr_SetString(PyExc_TypeError, - "GObject.connect_object requires at least 3 arguments"); - return NULL; - } - first = PySequence_GetSlice(args, 0, 3); - if (!PyArg_ParseTuple(first, "sOO:GObject.connect_object", - &name, &callback, &object)) { - Py_DECREF(first); - return NULL; - } - Py_DECREF(first); - if (!PyCallable_Check(callback)) { - PyErr_SetString(PyExc_TypeError, "second argument must be callable"); - return NULL; - } - - CHECK_GOBJECT(self); - - extra_args = PySequence_GetSlice(args, 3, len); - if (extra_args == NULL) - return NULL; - - ret = connect_helper(self, name, callback, extra_args, object, FALSE); - Py_DECREF(extra_args); - return ret; -} - -static PyObject * -pygobject_connect_object_after(PyGObject *self, PyObject *args) -{ - PyObject *first, *callback, *extra_args, *object, *ret; - gchar *name; - Py_ssize_t len; - - len = PyTuple_Size(args); - if (len < 3) { - PyErr_SetString(PyExc_TypeError, - "GObject.connect_object_after requires at least 3 arguments"); - return NULL; - } - first = PySequence_GetSlice(args, 0, 3); - if (!PyArg_ParseTuple(first, "sOO:GObject.connect_object_after", - &name, &callback, &object)) { - Py_DECREF(first); - return NULL; - } - Py_DECREF(first); - if (!PyCallable_Check(callback)) { - PyErr_SetString(PyExc_TypeError, "second argument must be callable"); - return NULL; - } - - CHECK_GOBJECT(self); - - extra_args = PySequence_GetSlice(args, 3, len); - if (extra_args == NULL) - return NULL; - - ret = connect_helper(self, name, callback, extra_args, object, TRUE); - Py_DECREF(extra_args); - return ret; -} - -static PyObject * -pygobject_emit(PyGObject *self, PyObject *args) -{ - guint signal_id, i, j; - Py_ssize_t len; - GQuark detail; - PyObject *first, *py_ret, *repr = NULL; - gchar *name; - GSignalQuery query; - GValue *params, ret = { 0, }; - - len = PyTuple_Size(args); - if (len < 1) { - PyErr_SetString(PyExc_TypeError,"GObject.emit needs at least one arg"); - return NULL; - } - first = PySequence_GetSlice(args, 0, 1); - if (!PyArg_ParseTuple(first, "s:GObject.emit", &name)) { - Py_DECREF(first); - return NULL; - } - Py_DECREF(first); - - CHECK_GOBJECT(self); - - if (!g_signal_parse_name(name, G_OBJECT_TYPE(self->obj), - &signal_id, &detail, TRUE)) { - repr = PyObject_Repr((PyObject*)self); - PyErr_Format(PyExc_TypeError, "%s: unknown signal name: %s", - PYGLIB_PyUnicode_AsString(repr), - name); - Py_DECREF(repr); - return NULL; - } - g_signal_query(signal_id, &query); - if (len != query.n_params + 1) { - gchar buf[128]; - - g_snprintf(buf, sizeof(buf), - "%d parameters needed for signal %s; %ld given", - query.n_params, name, (long int) (len - 1)); - PyErr_SetString(PyExc_TypeError, buf); - return NULL; - } - - params = g_new0(GValue, query.n_params + 1); - g_value_init(¶ms[0], G_OBJECT_TYPE(self->obj)); - g_value_set_object(¶ms[0], G_OBJECT(self->obj)); - - for (i = 0; i < query.n_params; i++) - g_value_init(¶ms[i + 1], - query.param_types[i] & ~G_SIGNAL_TYPE_STATIC_SCOPE); - for (i = 0; i < query.n_params; i++) { - PyObject *item = PyTuple_GetItem(args, i+1); - - if (pyg_value_from_pyobject(¶ms[i+1], item) < 0) { - gchar buf[128]; - g_snprintf(buf, sizeof(buf), - "could not convert type %s to %s required for parameter %d", - Py_TYPE(item)->tp_name, - G_VALUE_TYPE_NAME(¶ms[i+1]), i); - PyErr_SetString(PyExc_TypeError, buf); - - for (j = 0; j <= i; j++) - g_value_unset(¶ms[j]); - - g_free(params); - return NULL; - } - } - - if (query.return_type != G_TYPE_NONE) - g_value_init(&ret, query.return_type & ~G_SIGNAL_TYPE_STATIC_SCOPE); - - g_signal_emitv(params, signal_id, detail, &ret); - - for (i = 0; i < query.n_params + 1; i++) - g_value_unset(¶ms[i]); - - g_free(params); - if ((query.return_type & ~G_SIGNAL_TYPE_STATIC_SCOPE) != G_TYPE_NONE) { - py_ret = pyg_value_as_pyobject(&ret, TRUE); - g_value_unset(&ret); - } else { - Py_INCREF(Py_None); - py_ret = Py_None; - } - - return py_ret; -} - -static PyObject * -pygobject_chain_from_overridden(PyGObject *self, PyObject *args) -{ - GSignalInvocationHint *ihint; - guint signal_id, i; - Py_ssize_t len; - PyObject *py_ret; - const gchar *name; - GSignalQuery query; - GValue *params, ret = { 0, }; - - CHECK_GOBJECT(self); - - ihint = g_signal_get_invocation_hint(self->obj); - if (!ihint) { - PyErr_SetString(PyExc_TypeError, "could not find signal invocation " - "information for this object."); - return NULL; - } - - signal_id = ihint->signal_id; - name = g_signal_name(signal_id); - - len = PyTuple_Size(args); - if (signal_id == 0) { - PyErr_SetString(PyExc_TypeError, "unknown signal name"); - return NULL; - } - g_signal_query(signal_id, &query); - if (len != query.n_params) { - gchar buf[128]; - - g_snprintf(buf, sizeof(buf), - "%d parameters needed for signal %s; %ld given", - query.n_params, name, (long int) len); - PyErr_SetString(PyExc_TypeError, buf); - return NULL; - } - params = g_new0(GValue, query.n_params + 1); - g_value_init(¶ms[0], G_OBJECT_TYPE(self->obj)); - g_value_set_object(¶ms[0], G_OBJECT(self->obj)); - - for (i = 0; i < query.n_params; i++) - g_value_init(¶ms[i + 1], - query.param_types[i] & ~G_SIGNAL_TYPE_STATIC_SCOPE); - for (i = 0; i < query.n_params; i++) { - PyObject *item = PyTuple_GetItem(args, i); - - if (pyg_boxed_check(item, (query.param_types[i] & ~G_SIGNAL_TYPE_STATIC_SCOPE))) { - g_value_set_static_boxed(¶ms[i+1], pyg_boxed_get(item, void)); - } - else if (pyg_value_from_pyobject(¶ms[i+1], item) < 0) { - gchar buf[128]; - - g_snprintf(buf, sizeof(buf), - "could not convert type %s to %s required for parameter %d", - Py_TYPE(item)->tp_name, - g_type_name(G_VALUE_TYPE(¶ms[i+1])), i); - PyErr_SetString(PyExc_TypeError, buf); - for (i = 0; i < query.n_params + 1; i++) - g_value_unset(¶ms[i]); - g_free(params); - return NULL; - } - } - if (query.return_type != G_TYPE_NONE) - g_value_init(&ret, query.return_type & ~G_SIGNAL_TYPE_STATIC_SCOPE); - g_signal_chain_from_overridden(params, &ret); - for (i = 0; i < query.n_params + 1; i++) - g_value_unset(¶ms[i]); - g_free(params); - if (query.return_type != G_TYPE_NONE) { - py_ret = pyg_value_as_pyobject(&ret, TRUE); - g_value_unset(&ret); - } else { - Py_INCREF(Py_None); - py_ret = Py_None; - } - return py_ret; -} - - -static PyObject * -pygobject_weak_ref(PyGObject *self, PyObject *args) -{ - int len; - PyObject *callback = NULL, *user_data = NULL; - PyObject *retval; - - CHECK_GOBJECT(self); - - if ((len = PySequence_Length(args)) >= 1) { - callback = PySequence_ITEM(args, 0); - user_data = PySequence_GetSlice(args, 1, len); - } - retval = pygobject_weak_ref_new(self->obj, callback, user_data); - Py_XDECREF(callback); - Py_XDECREF(user_data); - return retval; -} - - -static PyObject * -pygobject_copy(PyGObject *self) -{ - PyErr_SetString(PyExc_TypeError, - "GObject descendants' instances are non-copyable"); - return NULL; -} - -static PyObject * -pygobject_deepcopy(PyGObject *self, PyObject *args) -{ - PyErr_SetString(PyExc_TypeError, - "GObject descendants' instances are non-copyable"); - return NULL; -} - - -static PyObject * -pygobject_disconnect_by_func(PyGObject *self, PyObject *args) -{ - PyObject *pyfunc = NULL, *repr = NULL; - GClosure *closure = NULL; - guint retval; - - CHECK_GOBJECT(self); - - if (!PyArg_ParseTuple(args, "O:GObject.disconnect_by_func", &pyfunc)) - return NULL; - - if (!PyCallable_Check(pyfunc)) { - PyErr_SetString(PyExc_TypeError, "first argument must be callable"); - return NULL; - } - - closure = gclosure_from_pyfunc(self, pyfunc); - if (!closure) { - repr = PyObject_Repr((PyObject*)pyfunc); - PyErr_Format(PyExc_TypeError, "nothing connected to %s", - PYGLIB_PyUnicode_AsString(repr)); - Py_DECREF(repr); - return NULL; - } - - retval = g_signal_handlers_disconnect_matched(self->obj, - G_SIGNAL_MATCH_CLOSURE, - 0, 0, - closure, - NULL, NULL); - return PYGLIB_PyLong_FromLong(retval); -} - -static PyObject * -pygobject_handler_block_by_func(PyGObject *self, PyObject *args) -{ - PyObject *pyfunc = NULL, *repr = NULL; - GClosure *closure = NULL; - guint retval; - - CHECK_GOBJECT(self); - - if (!PyArg_ParseTuple(args, "O:GObject.handler_block_by_func", &pyfunc)) - return NULL; - - if (!PyCallable_Check(pyfunc)) { - PyErr_SetString(PyExc_TypeError, "first argument must be callable"); - return NULL; - } - - closure = gclosure_from_pyfunc(self, pyfunc); - if (!closure) { - repr = PyObject_Repr((PyObject*)pyfunc); - PyErr_Format(PyExc_TypeError, "nothing connected to %s", - PYGLIB_PyUnicode_AsString(repr)); - Py_DECREF(repr); - return NULL; - } - - retval = g_signal_handlers_block_matched(self->obj, - G_SIGNAL_MATCH_CLOSURE, - 0, 0, - closure, - NULL, NULL); - return PYGLIB_PyLong_FromLong(retval); -} - -static PyObject * -pygobject_handler_unblock_by_func(PyGObject *self, PyObject *args) -{ - PyObject *pyfunc = NULL, *repr = NULL; - GClosure *closure = NULL; - guint retval; - - CHECK_GOBJECT(self); - - if (!PyArg_ParseTuple(args, "O:GObject.handler_unblock_by_func", &pyfunc)) - return NULL; - - if (!PyCallable_Check(pyfunc)) { - PyErr_SetString(PyExc_TypeError, "first argument must be callable"); - return NULL; - } - - closure = gclosure_from_pyfunc(self, pyfunc); - if (!closure) { - repr = PyObject_Repr((PyObject*)pyfunc); - PyErr_Format(PyExc_TypeError, "nothing connected to %s", - PYGLIB_PyUnicode_AsString(repr)); - Py_DECREF(repr); - return NULL; - } - - retval = g_signal_handlers_unblock_matched(self->obj, - G_SIGNAL_MATCH_CLOSURE, - 0, 0, - closure, - NULL, NULL); - return PYGLIB_PyLong_FromLong(retval); -} - - -static PyMethodDef pygobject_methods[] = { - { "get_property", (PyCFunction)pygobject_get_property, METH_VARARGS }, - { "get_properties", (PyCFunction)pygobject_get_properties, METH_VARARGS }, - { "set_property", (PyCFunction)pygobject_set_property, METH_VARARGS }, - { "set_properties", (PyCFunction)pygobject_set_properties, METH_VARARGS|METH_KEYWORDS }, - { "bind_property", (PyCFunction)pygobject_bind_property, METH_VARARGS|METH_KEYWORDS }, - { "connect", (PyCFunction)pygobject_connect, METH_VARARGS }, - { "connect_after", (PyCFunction)pygobject_connect_after, METH_VARARGS }, - { "connect_object", (PyCFunction)pygobject_connect_object, METH_VARARGS }, - { "connect_object_after", (PyCFunction)pygobject_connect_object_after, METH_VARARGS }, - { "disconnect_by_func", (PyCFunction)pygobject_disconnect_by_func, METH_VARARGS }, - { "handler_block_by_func", (PyCFunction)pygobject_handler_block_by_func, METH_VARARGS }, - { "handler_unblock_by_func", (PyCFunction)pygobject_handler_unblock_by_func, METH_VARARGS }, - { "emit", (PyCFunction)pygobject_emit, METH_VARARGS }, - { "chain", (PyCFunction)pygobject_chain_from_overridden,METH_VARARGS }, - { "weak_ref", (PyCFunction)pygobject_weak_ref, METH_VARARGS }, - { "__copy__", (PyCFunction)pygobject_copy, METH_NOARGS }, - { "__deepcopy__", (PyCFunction)pygobject_deepcopy, METH_VARARGS }, - { NULL, NULL, 0 } -}; - - -static PyObject * -pygobject_get_dict(PyGObject *self, void *closure) -{ - if (self->inst_dict == NULL) { - self->inst_dict = PyDict_New(); - if (self->inst_dict == NULL) - return NULL; - if (G_LIKELY(self->obj)) - pygobject_switch_to_toggle_ref(self); - } - Py_INCREF(self->inst_dict); - return self->inst_dict; -} - -static PyObject * -pygobject_get_refcount(PyGObject *self, void *closure) -{ - if (self->obj == NULL) { - PyErr_Format(PyExc_TypeError, "GObject instance is not yet created"); - return NULL; - } - return PYGLIB_PyLong_FromLong(self->obj->ref_count); -} - -static PyObject * -pygobject_get_pointer(PyGObject *self, void *closure) -{ - return PYGLIB_CPointer_WrapPointer (self->obj, NULL); -} - -static int -pygobject_setattro(PyObject *self, PyObject *name, PyObject *value) -{ - int res; - PyGObject *gself = (PyGObject *) self; - PyObject *inst_dict_before = gself->inst_dict; - /* call parent type's setattro */ - res = PyGObject_Type.tp_base->tp_setattro(self, name, value); - if (inst_dict_before == NULL && gself->inst_dict != NULL) { - if (G_LIKELY(gself->obj)) - pygobject_switch_to_toggle_ref(gself); - } - return res; -} - -static PyGetSetDef pygobject_getsets[] = { - { "__dict__", (getter)pygobject_get_dict, (setter)0 }, - { "__grefcount__", (getter)pygobject_get_refcount, (setter)0, }, - { "__gpointer__", (getter)pygobject_get_pointer, (setter)0, }, - { NULL, 0, 0 } -}; - -/* ------------------------------------ */ -/* ****** GObject weak reference ****** */ -/* ------------------------------------ */ - -typedef struct { - PyObject_HEAD - GObject *obj; - PyObject *callback; - PyObject *user_data; - gboolean have_floating_ref; -} PyGObjectWeakRef; - -PYGLIB_DEFINE_TYPE("gi._gobject.GObjectWeakRef", PyGObjectWeakRef_Type, PyGObjectWeakRef); - -static int -pygobject_weak_ref_traverse(PyGObjectWeakRef *self, visitproc visit, void *arg) -{ - if (self->callback && visit(self->callback, arg) < 0) - return -1; - if (self->user_data && visit(self->user_data, arg) < 0) - return -1; - return 0; -} - -static void -pygobject_weak_ref_notify(PyGObjectWeakRef *self, GObject *dummy) -{ - self->obj = NULL; - if (self->callback) { - PyObject *retval; - PyGILState_STATE state = pyglib_gil_state_ensure(); - retval = PyObject_Call(self->callback, self->user_data, NULL); - if (retval) { - if (retval != Py_None) - PyErr_Format(PyExc_TypeError, - "GObject weak notify callback returned a value" - " of type %s, should return None", - Py_TYPE(retval)->tp_name); - Py_DECREF(retval); - PyErr_Print(); - } else - PyErr_Print(); - Py_CLEAR(self->callback); - Py_CLEAR(self->user_data); - if (self->have_floating_ref) { - self->have_floating_ref = FALSE; - Py_DECREF((PyObject *) self); - } - pyglib_gil_state_release(state); - } -} - -static inline int -pygobject_weak_ref_clear(PyGObjectWeakRef *self) -{ - Py_CLEAR(self->callback); - Py_CLEAR(self->user_data); - if (self->obj) { - g_object_weak_unref(self->obj, (GWeakNotify) pygobject_weak_ref_notify, self); - self->obj = NULL; - } - return 0; -} - -static void -pygobject_weak_ref_dealloc(PyGObjectWeakRef *self) -{ - PyObject_GC_UnTrack((PyObject *)self); - pygobject_weak_ref_clear(self); - PyObject_GC_Del(self); -} - -static PyObject * -pygobject_weak_ref_new(GObject *obj, PyObject *callback, PyObject *user_data) -{ - PyGObjectWeakRef *self; - - self = PyObject_GC_New(PyGObjectWeakRef, &PyGObjectWeakRef_Type); - self->callback = callback; - self->user_data = user_data; - Py_XINCREF(self->callback); - Py_XINCREF(self->user_data); - self->obj = obj; - g_object_weak_ref(self->obj, (GWeakNotify) pygobject_weak_ref_notify, self); - if (callback != NULL) { - /* when we have a callback, we should INCREF the weakref - * object to make it stay alive even if it goes out of scope */ - self->have_floating_ref = TRUE; - Py_INCREF((PyObject *) self); - } - return (PyObject *) self; -} - -static PyObject * -pygobject_weak_ref_unref(PyGObjectWeakRef *self, PyObject *args) -{ - if (!self->obj) { - PyErr_SetString(PyExc_ValueError, "weak ref already unreffed"); - return NULL; - } - g_object_weak_unref(self->obj, (GWeakNotify) pygobject_weak_ref_notify, self); - self->obj = NULL; - if (self->have_floating_ref) { - self->have_floating_ref = FALSE; - Py_DECREF(self); - } - Py_INCREF(Py_None); - return Py_None; -} - -static PyMethodDef pygobject_weak_ref_methods[] = { - { "unref", (PyCFunction)pygobject_weak_ref_unref, METH_NOARGS}, - { NULL, NULL, 0} -}; - -static PyObject * -pygobject_weak_ref_call(PyGObjectWeakRef *self, PyObject *args, PyObject *kw) -{ - static char *argnames[] = {NULL}; - - if (!PyArg_ParseTupleAndKeywords(args, kw, ":__call__", argnames)) - return NULL; - - if (self->obj) - return pygobject_new(self->obj); - else { - Py_INCREF(Py_None); - return Py_None; - } -} - -static gpointer -pyobject_copy(gpointer boxed) -{ - PyObject *object = boxed; - PyGILState_STATE state; - - state = pyglib_gil_state_ensure(); - Py_INCREF(object); - pyglib_gil_state_release(state); - return object; -} - -static void -pyobject_free(gpointer boxed) -{ - PyObject *object = boxed; - PyGILState_STATE state; - - state = pyglib_gil_state_ensure(); - Py_DECREF(object); - pyglib_gil_state_release(state); -} - -void -pygobject_object_register_types(PyObject *d) -{ - PyObject *o, *descr; - - pygobject_custom_key = g_quark_from_static_string("PyGObject::custom"); - pygobject_class_key = g_quark_from_static_string("PyGObject::class"); - pygobject_class_init_key = g_quark_from_static_string("PyGObject::class-init"); - pygobject_wrapper_key = g_quark_from_static_string("PyGObject::wrapper"); - pygobject_has_updated_constructor_key = - g_quark_from_static_string("PyGObject::has-updated-constructor"); - pygobject_instance_data_key = g_quark_from_static_string("PyGObject::instance-data"); - - /* GObject */ - if (!PY_TYPE_OBJECT) - PY_TYPE_OBJECT = g_boxed_type_register_static("PyObject", - pyobject_copy, - pyobject_free); - PyGObject_Type.tp_dealloc = (destructor)pygobject_dealloc; - PyGObject_Type.tp_richcompare = pygobject_richcompare; - PyGObject_Type.tp_repr = (reprfunc)pygobject_repr; - PyGObject_Type.tp_hash = (hashfunc)pygobject_hash; - PyGObject_Type.tp_setattro = (setattrofunc)pygobject_setattro; - PyGObject_Type.tp_flags = (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | - Py_TPFLAGS_HAVE_GC); - PyGObject_Type.tp_traverse = (traverseproc)pygobject_traverse; - PyGObject_Type.tp_clear = (inquiry)pygobject_clear; - PyGObject_Type.tp_weaklistoffset = offsetof(PyGObject, weakreflist); - PyGObject_Type.tp_methods = pygobject_methods; - PyGObject_Type.tp_getset = pygobject_getsets; - PyGObject_Type.tp_dictoffset = offsetof(PyGObject, inst_dict); - PyGObject_Type.tp_init = (initproc)pygobject_init; - PyGObject_Type.tp_free = (freefunc)pygobject_free; - PyGObject_Type.tp_alloc = PyType_GenericAlloc; - PyGObject_Type.tp_new = PyType_GenericNew; - pygobject_register_class(d, "GObject", G_TYPE_OBJECT, - &PyGObject_Type, NULL); - PyDict_SetItemString(PyGObject_Type.tp_dict, "__gdoc__", - pyg_object_descr_doc_get()); - - /* GProps */ - PyGProps_Type.tp_dealloc = (destructor)PyGProps_dealloc; - PyGProps_Type.tp_as_sequence = (PySequenceMethods*)&_PyGProps_as_sequence; - PyGProps_Type.tp_getattro = (getattrofunc)PyGProps_getattro; - PyGProps_Type.tp_setattro = (setattrofunc)PyGProps_setattro; - PyGProps_Type.tp_flags = Py_TPFLAGS_DEFAULT|Py_TPFLAGS_HAVE_GC; - PyGProps_Type.tp_doc = "The properties of the GObject accessible as " - "Python attributes."; - PyGProps_Type.tp_traverse = (traverseproc)pygobject_props_traverse; - PyGProps_Type.tp_iter = (getiterfunc)pygobject_props_get_iter; - PyGProps_Type.tp_methods = pygobject_props_methods; - if (PyType_Ready(&PyGProps_Type) < 0) - return; - - /* GPropsDescr */ - PyGPropsDescr_Type.tp_flags = Py_TPFLAGS_DEFAULT; - PyGPropsDescr_Type.tp_descr_get = pyg_props_descr_descr_get; - if (PyType_Ready(&PyGPropsDescr_Type) < 0) - return; - descr = PyObject_New(PyObject, &PyGPropsDescr_Type); - PyDict_SetItemString(PyGObject_Type.tp_dict, "props", descr); - PyDict_SetItemString(PyGObject_Type.tp_dict, "__module__", - o=PYGLIB_PyUnicode_FromString("gi._gobject._gobject")); - Py_DECREF(o); - - /* GPropsIter */ - PyGPropsIter_Type.tp_dealloc = (destructor)pyg_props_iter_dealloc; - PyGPropsIter_Type.tp_flags = Py_TPFLAGS_DEFAULT; - PyGPropsIter_Type.tp_doc = "GObject properties iterator"; - PyGPropsIter_Type.tp_iternext = (iternextfunc)pygobject_props_iter_next; - if (PyType_Ready(&PyGPropsIter_Type) < 0) - return; - - PyGObjectWeakRef_Type.tp_dealloc = (destructor)pygobject_weak_ref_dealloc; - PyGObjectWeakRef_Type.tp_call = (ternaryfunc)pygobject_weak_ref_call; - PyGObjectWeakRef_Type.tp_flags = Py_TPFLAGS_DEFAULT|Py_TPFLAGS_HAVE_GC; - PyGObjectWeakRef_Type.tp_doc = "A GObject weak reference"; - PyGObjectWeakRef_Type.tp_traverse = (traverseproc)pygobject_weak_ref_traverse; - PyGObjectWeakRef_Type.tp_clear = (inquiry)pygobject_weak_ref_clear; - PyGObjectWeakRef_Type.tp_methods = pygobject_weak_ref_methods; - if (PyType_Ready(&PyGObjectWeakRef_Type) < 0) - return; - PyDict_SetItemString(d, "GObjectWeakRef", (PyObject *) &PyGObjectWeakRef_Type); -} diff --git a/gi/pygparamspec.c b/gi/pygparamspec.c index ff532436..0982b995 100644 --- a/gi/pygparamspec.c +++ b/gi/pygparamspec.c @@ -24,8 +24,11 @@ #endif #include +#include -#include "pygobject-private.h" +#include "pygenum.h" +#include "pygflags.h" +#include "pygtype.h" #include "pygparamspec.h" PYGLIB_DEFINE_TYPE("gobject.GParamSpec", PyGParamSpec_Type, PyGParamSpec); diff --git a/gi/pygpointer.c b/gi/pygpointer.c index d160fff1..64ca9838 100644 --- a/gi/pygpointer.c +++ b/gi/pygpointer.c @@ -23,8 +23,9 @@ #endif #include -#include "pygobject-private.h" +#include #include "pygpointer.h" +#include "pygtype.h" #include "pygi-type.h" diff --git a/gi/pygpointer.h b/gi/pygpointer.h index 792846e3..363362c2 100644 --- a/gi/pygpointer.h +++ b/gi/pygpointer.h @@ -20,6 +20,14 @@ #ifndef __PYGOBJECT_POINTER_H__ #define __PYGOBJECT_POINTER_H__ +extern GQuark pygpointer_class_key; + +extern PyTypeObject PyGPointer_Type; + +void pyg_register_pointer (PyObject *dict, const gchar *class_name, + GType pointer_type, PyTypeObject *type); +PyObject * pyg_pointer_new (GType pointer_type, gpointer pointer); + void pygobject_pointer_register_types(PyObject *d); #endif /* __PYGOBJECT_POINTER_H__ */ diff --git a/gi/pygtype.c b/gi/pygtype.c index a3784c86..32132ad4 100644 --- a/gi/pygtype.c +++ b/gi/pygtype.c @@ -24,9 +24,14 @@ #include -#include "pygobject-private.h" +#include "pygobject-object.h" +#include "pygboxed.h" +#include "pygenum.h" +#include "pygflags.h" #include "pygparamspec.h" #include "pygtype.h" +#include "pygpointer.h" +#include "pyginterface.h" #include "pygi-type.h" #include "pygi-value.h" @@ -945,26 +950,6 @@ pyg_signal_class_closure_get(void) return closure; } -GClosure * -gclosure_from_pyfunc(PyGObject *object, PyObject *func) -{ - GSList *l; - PyGObjectData *inst_data; - inst_data = pyg_object_peek_inst_data(object->obj); - if (inst_data) { - for (l = inst_data->closures; l; l = l->next) { - PyGClosure *pyclosure = l->data; - int res = PyObject_RichCompareBool(pyclosure->callback, func, Py_EQ); - if (res == -1) { - PyErr_Clear(); /* Is there anything else to do? */ - } else if (res) { - return (GClosure*)pyclosure; - } - } - } - return NULL; -} - /* ----- __doc__ descriptor for GObject and GInterface ----- */ static void diff --git a/gi/pygtype.h b/gi/pygtype.h index f21cf1f7..82c2523c 100644 --- a/gi/pygtype.h +++ b/gi/pygtype.h @@ -23,6 +23,18 @@ #include #include +#include "pygobject-internal.h" + +#define PYGOBJECT_REGISTER_GTYPE(d, type, name, gtype) \ + { \ + PyObject *o; \ + PYGLIB_REGISTER_TYPE(d, type, name); \ + PyDict_SetItemString(type.tp_dict, "__gtype__", \ + o=pyg_type_wrapper_new(gtype)); \ + Py_DECREF(o); \ +} + +extern PyTypeObject PyGTypeWrapper_Type; typedef PyObject *(* fromvaluefunc)(const GValue *value); typedef int (*tovaluefunc)(GValue *value, PyObject *obj); @@ -34,10 +46,23 @@ typedef struct { PyGTypeMarshal *pyg_type_lookup(GType type); +gboolean pyg_gtype_is_custom (GType gtype); + void pyg_register_gtype_custom(GType gtype, fromvaluefunc from_func, tovaluefunc to_func); void pygobject_type_register_types(PyObject *d); +PyObject *pyg_object_descr_doc_get(void); +PyObject *pyg_type_wrapper_new (GType type); +GType pyg_type_from_object_strict (PyObject *obj, gboolean strict); +GType pyg_type_from_object (PyObject *obj); + +int pyg_pyobj_to_unichar_conv (PyObject* py_obj, void* ptr); + +GClosure *pyg_closure_new(PyObject *callback, PyObject *extra_args, PyObject *swap_data); +GClosure *pyg_signal_class_closure_get(void); +void pyg_closure_set_exception_handler(GClosure *closure, + PyClosureExceptionHandler handler); #endif /* __PYGOBJECT_TYPE_H__ */ -- cgit v1.2.1