summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorGustavo J. A. M. Carneiro <gjc@src.gnome.org>2005-07-09 18:25:32 +0000
committerGustavo J. A. M. Carneiro <gjc@src.gnome.org>2005-07-09 18:25:32 +0000
commit83a43997b2136c6f5337219ddfe6aa6829d66fae (patch)
treee31a18b2a0f0fcc7cb55849c88c2afa12aa85d3d
parent49eabd6c5ab0b3c849bd03bd2149b47e50f19aee (diff)
downloadpygobject-83a43997b2136c6f5337219ddfe6aa6829d66fae.tar.gz
pygobject-83a43997b2136c6f5337219ddfe6aa6829d66fae.tar.xz
pygobject-83a43997b2136c6f5337219ddfe6aa6829d66fae.zip
Bug 161177: Allow creation of python classes from g_object_new
-rw-r--r--gobject/gobjectmodule.c172
-rw-r--r--gobject/pygobject-private.h15
-rw-r--r--gobject/pygobject.c18
-rw-r--r--gobject/pygobject.h11
-rw-r--r--tests/Makefile.am2
-rw-r--r--tests/runtests.py2
-rw-r--r--tests/test_properties.py35
-rw-r--r--tests/test_subtype.py33
-rw-r--r--tests/testhelpermodule.c75
-rw-r--r--tests/testmodule.py9
10 files changed, 347 insertions, 25 deletions
diff --git a/gobject/gobjectmodule.c b/gobject/gobjectmodule.c
index e151306..19017ac 100644
--- a/gobject/gobjectmodule.c
+++ b/gobject/gobjectmodule.c
@@ -26,6 +26,7 @@
#include "pygobject-private.h"
#include "pythread.h"
+#include <gobject/gvaluecollector.h>
static PyObject *gerror_exc = NULL;
static gboolean use_gil_state_api = FALSE;
@@ -40,6 +41,9 @@ GQuark pygboxed_marshal_key;
GQuark pygenum_class_key;
GQuark pygflags_class_key;
GQuark pygpointer_class_key;
+GQuark pygobject_has_updated_constructor_key;
+
+
static void pyg_flags_add_constants(PyObject *module, GType flags_type,
const gchar *strip_prefix);
@@ -1028,6 +1032,56 @@ get_type_name_for_class(PyTypeObject *class)
return type_name;
}
+
+static GStaticPrivate pygobject_contruction_wrapper = G_STATIC_PRIVATE_INIT;
+
+static inline void
+pygobject_init_wrapper_set(PyObject *wrapper)
+{
+ g_static_private_set(&pygobject_contruction_wrapper, wrapper, NULL);
+}
+
+static inline PyObject *
+pygobject_init_wrapper_get(void)
+{
+ return (PyObject *) g_static_private_get(&pygobject_contruction_wrapper);
+}
+
+static void
+pygobject__g_instance_init(GTypeInstance *instance,
+ gpointer g_class)
+{
+ GObject *object = (GObject *) instance;
+ PyObject *wrapper, *args, *kwargs;
+
+ if (!g_type_get_qdata(G_OBJECT_TYPE(object), pygobject_has_updated_constructor_key))
+ return;
+
+ wrapper = g_object_get_qdata(object, pygobject_wrapper_key);
+ if (wrapper == NULL) {
+ wrapper = pygobject_init_wrapper_get();
+ if (wrapper) {
+ g_object_set_qdata(object, pygobject_wrapper_key, wrapper);
+ }
+ }
+ pygobject_init_wrapper_set(NULL);
+ if (wrapper == NULL) {
+ /* this looks like a python object created through
+ * g_object_new -> we have no python wrapper, so create it
+ * now */
+ PyGILState_STATE state;
+ state = pyg_gil_state_ensure();
+ wrapper = pygobject_new_full(object, FALSE);
+ args = PyTuple_New(0);
+ kwargs = PyDict_New();
+ if (wrapper->ob_type->tp_init(wrapper, args, kwargs))
+ PyErr_Print();
+ Py_DECREF(args);
+ Py_DECREF(kwargs);
+ pyg_gil_state_release(state);
+ }
+}
+
int
pyg_type_register(PyTypeObject *class, char *type_name)
{
@@ -1036,6 +1090,7 @@ pyg_type_register(PyTypeObject *class, char *type_name)
gint i;
GTypeQuery query;
gpointer gclass;
+ gpointer has_new_constructor_api;
GTypeInfo type_info = {
0, /* class_size */
@@ -1048,7 +1103,7 @@ pyg_type_register(PyTypeObject *class, char *type_name)
0, /* instance_size */
0, /* n_preallocs */
- (GInstanceInitFunc) NULL
+ (GInstanceInitFunc) pygobject__g_instance_init
};
@@ -1088,6 +1143,16 @@ pyg_type_register(PyTypeObject *class, char *type_name)
PyDict_SetItemString(class->tp_dict, "__gtype__", gtype);
Py_DECREF(gtype);
+ /* propagate new constructor API compatility flag from parent to child type */
+ has_new_constructor_api = g_type_get_qdata(parent_type,
+ pygobject_has_updated_constructor_key);
+ if (has_new_constructor_api == NULL)
+ g_warning("Constructor wrapper for %s needs to be updated to the new API",
+ g_type_name(parent_type));
+ else
+ g_type_set_qdata(instance_type, pygobject_has_updated_constructor_key,
+ has_new_constructor_api);
+
/* if no __doc__, set it to the auto doc descriptor */
if (PyDict_GetItemString(class->tp_dict, "__doc__") == NULL) {
PyDict_SetItemString(class->tp_dict, "__doc__",
@@ -1572,8 +1637,12 @@ pyg_object_new (PyGObject *self, PyObject *args, PyObject *kwargs)
g_type_class_unref(class);
if (obj)
- return pygobject_new ((GObject *)obj);
- return NULL;
+ self = (PyGObject *) pygobject_new ((GObject *)obj);
+ else
+ self = NULL;
+ g_object_unref(obj);
+
+ return (PyObject *) self;
}
static gint
@@ -2455,6 +2524,95 @@ pyg_parse_constructor_args(GType obj_type,
return TRUE;
}
+int
+pygobject_constructv(PyGObject *self,
+ guint n_parameters,
+ GParameter *parameters)
+{
+ if (self->obj == NULL) {
+ pygobject_init_wrapper_set((PyObject *) self);
+ self->obj = g_object_newv(pyg_type_from_object((PyObject *) self),
+ n_parameters, parameters);
+ pygobject_init_wrapper_set(NULL);
+ pygobject_register_wrapper((PyObject *) self);
+ } else {
+ int i;
+ for (i = 0; i < n_parameters; ++i)
+ g_object_set_property(self->obj, parameters[i].name, &parameters[i].value);
+ }
+ return 0;
+}
+
+/* This function is mostly juste copy-paste from g_object_new, but
+ * calls pygobject_constructv instead of g_object_newv */
+int
+pygobject_construct(PyGObject *self, const char *first_property_name, ...)
+{
+ va_list var_args;
+ GObjectClass *class;
+ GParameter *params;
+ const gchar *name;
+ guint n_params = 0, n_alloced_params = 16;
+ GType object_type = pyg_type_from_object((PyObject *) self);
+ int retval;
+
+ if (!first_property_name)
+ return pygobject_constructv(self, 0, NULL);
+
+ va_start(var_args, first_property_name);
+ class = g_type_class_ref(object_type);
+
+ params = g_new(GParameter, n_alloced_params);
+ name = first_property_name;
+ while (name)
+ {
+ gchar *error = NULL;
+ GParamSpec *pspec = g_object_class_find_property(class, name);
+
+ if (!pspec)
+ {
+ g_warning("%s: object class `%s' has no property named `%s'",
+ G_STRFUNC,
+ g_type_name(object_type),
+ name);
+ break;
+ }
+ if (n_params >= n_alloced_params)
+ {
+ n_alloced_params += 16;
+ params = g_renew(GParameter, params, n_alloced_params);
+ }
+ params[n_params].name = name;
+ params[n_params].value.g_type = 0;
+ g_value_init(&params[n_params].value, G_PARAM_SPEC_VALUE_TYPE(pspec));
+ G_VALUE_COLLECT(&params[n_params].value, var_args, 0, &error);
+ if (error)
+ {
+ g_warning("%s: %s", G_STRFUNC, error);
+ g_free(error);
+ g_value_unset(&params[n_params].value);
+ break;
+ }
+ n_params++;
+ name = va_arg(var_args, gchar*);
+ }
+
+ retval = pygobject_constructv(self, n_params, params);
+
+ while (n_params--)
+ g_value_unset(&params[n_params].value);
+ g_free(params);
+ va_end(var_args);
+ g_type_class_unref(class);
+ return retval;
+}
+
+void
+pyg_set_object_has_new_constructor(GType type)
+{
+ g_type_set_qdata(type, pygobject_has_updated_constructor_key, GINT_TO_POINTER(1));
+}
+
/* ----------------- gobject module initialisation -------------- */
struct _PyGObject_Functions pygobject_api_functions = {
@@ -2521,7 +2679,10 @@ struct _PyGObject_Functions pygobject_api_functions = {
pyg_register_class_init,
pyg_register_interface_info,
- pyg_closure_set_exception_handler
+ pyg_closure_set_exception_handler,
+ pygobject_constructv,
+ pygobject_construct,
+ pyg_set_object_has_new_constructor
};
#define REGISTER_TYPE(d, type, name) \
@@ -2560,6 +2721,8 @@ initgobject(void)
pyginterface_type_key = g_quark_from_static_string("PyGInterface::type");
pyginterface_info_key = g_quark_from_static_string("PyGInterface::info");
pygpointer_class_key = g_quark_from_static_string("PyGPointer::class");
+ pygobject_has_updated_constructor_key =\
+ g_quark_from_static_string("PyGObject::has-updated-constructor");
REGISTER_TYPE(d, PyGTypeWrapper_Type, "GType");
@@ -2582,6 +2745,7 @@ initgobject(void)
&PyGObject_Type, NULL);
PyDict_SetItemString(PyGObject_Type.tp_dict, "__gdoc__",
pyg_object_descr_doc_get());
+ pyg_set_object_has_new_constructor(G_TYPE_OBJECT);
/* GObject properties descriptor */
if (PyType_Ready(&PyGProps_Type) < 0)
diff --git a/gobject/pygobject-private.h b/gobject/pygobject-private.h
index 4fd48e4..22bcab6 100644
--- a/gobject/pygobject-private.h
+++ b/gobject/pygobject-private.h
@@ -51,9 +51,17 @@ extern GQuark pygobject_class_init_key;
extern GQuark pygobject_class_key;
extern GQuark pygobject_wrapper_key;
extern GQuark pygpointer_class_key;
-
-void pyg_destroy_notify (gpointer user_data);
-gboolean pyg_error_check(GError **error);
+extern GQuark pygobject_has_updated_constructor_key;
+
+void pyg_destroy_notify (gpointer user_data);
+gboolean pyg_error_check (GError **error);
+int pygobject_constructv (PyGObject *self,
+ guint n_parameters,
+ GParameter *parameters);
+int pygobject_construct (PyGObject *self,
+ const char *first_property_name,
+ ...);
+void pyg_set_object_has_new_constructor (GType gtype);
/* from pygtype.h */
extern PyTypeObject PyGTypeWrapper_Type;
@@ -103,6 +111,7 @@ void pygobject_register_class (PyObject *dict,
PyObject *bases);
void pygobject_register_wrapper (PyObject *self);
PyObject * pygobject_new (GObject *obj);
+PyObject * pygobject_new_full (GObject *obj, gboolean sink);
PyTypeObject *pygobject_lookup_class (GType gtype);
void pygobject_watch_closure (PyObject *self, GClosure *closure);
void pygobject_register_sinkfunc(GType type,
diff --git a/gobject/pygobject.c b/gobject/pygobject.c
index 1263554..1fe837d 100644
--- a/gobject/pygobject.c
+++ b/gobject/pygobject.c
@@ -660,6 +660,7 @@ pygobject_lookup_class(GType gtype)
/**
* pygobject_new:
* @obj: a GObject instance.
+ * @sink: whether to sink any floating reference found on the GObject.
*
* This function gets a reference to a wrapper for the given GObject
* instance. If a wrapper has already been created, a new reference
@@ -669,7 +670,7 @@ pygobject_lookup_class(GType gtype)
* Returns: a reference to the wrapper for the GObject.
*/
PyObject *
-pygobject_new(GObject *obj)
+pygobject_new_full(GObject *obj, gboolean sink)
{
PyGObject *self;
@@ -695,7 +696,8 @@ pygobject_new(GObject *obj)
pyg_begin_allow_threads;
self->obj = g_object_ref(obj);
pyg_end_allow_threads;
- sink_object(self->obj);
+ if (sink)
+ sink_object(self->obj);
self->inst_dict = NULL;
self->weakreflist = NULL;
@@ -711,6 +713,12 @@ pygobject_new(GObject *obj)
return (PyObject *)self;
}
+PyObject *
+pygobject_new(GObject *obj)
+{
+ return pygobject_new_full(obj, TRUE);
+}
+
static void
pygobject_unwatch_closure(gpointer data, GClosure *closure)
{
@@ -943,11 +951,7 @@ pygobject_init(PyGObject *self, PyObject *args, PyObject *kwargs)
n_params++;
}
}
-
- self->obj = g_object_newv(object_type, n_params, params);
- if (self->obj)
- pygobject_register_wrapper((PyObject *)self);
- else
+ if (pygobject_constructv(self, n_params, params))
PyErr_SetString (PyExc_RuntimeError, "could not create object");
cleanup:
diff --git a/gobject/pygobject.h b/gobject/pygobject.h
index 18c50c0..3bba3ac 100644
--- a/gobject/pygobject.h
+++ b/gobject/pygobject.h
@@ -166,6 +166,13 @@ struct _PyGObject_Functions {
void (*register_class_init) (GType gtype, PyGClassInitFunc class_init);
void (*register_interface_info) (GType gtype, const GInterfaceInfo *info);
void (*closure_set_exception_handler) (GClosure *closure, PyClosureExceptionHandler handler);
+ int (*pygobject_constructv) (PyGObject *self,
+ guint n_parameters,
+ GParameter *parameters);
+ int (*pygobject_construct) (PyGObject *self,
+ const char *first_property_name,
+ ...);
+ void (*set_object_has_new_constructor) (GType type);
};
#ifndef _INSIDE_PYGOBJECT_
@@ -222,6 +229,10 @@ struct _PyGObject_Functions *_PyGObject_API;
#define pyg_enable_threads (_PyGObject_API->enable_threads)
#define pyg_register_class_init (_PyGObject_API->register_class_init)
#define pyg_register_interface_info (_PyGObject_API->register_interface_info)
+#define pygobject_construct (_PyGObject_API->pygobject_construct)
+#define pygobject_constructv (_PyGObject_API->pygobject_constructv)
+#define pyg_set_object_has_new_constructor (_PyGObject_API->set_object_has_new_constructor)
+
#define pyg_block_threads() G_STMT_START { \
if (_PyGObject_API->block_threads != NULL) \
diff --git a/tests/Makefile.am b/tests/Makefile.am
index 0f8ac7b..25a1a6d 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -6,7 +6,7 @@ INCLUDES = \
$(GTK_CFLAGS) \
-I$(top_srcdir)/gobject
-EXTRA_DIST = $(tests) common.py runtests.py test-thread.h test-unknown.h
+EXTRA_DIST = $(tests) common.py runtests.py test-thread.h test-unknown.h testmodule.py
noinst_LTLIBRARIES = testhelper.la
linked_LIBS = testhelper.la
diff --git a/tests/runtests.py b/tests/runtests.py
index 3ae7185..42f6a0c 100644
--- a/tests/runtests.py
+++ b/tests/runtests.py
@@ -21,7 +21,7 @@ else:
common.importModules(buildDir=buildDir,
srcDir=srcDir)
-SKIP_FILES = ['common', 'runtests']
+SKIP_FILES = ['common', 'runtests', 'testmodule']
dir = os.path.split(os.path.abspath(__file__))[0]
os.chdir(dir)
diff --git a/tests/test_properties.py b/tests/test_properties.py
index db620ea..970b646 100644
--- a/tests/test_properties.py
+++ b/tests/test_properties.py
@@ -2,27 +2,38 @@
import unittest
from common import testhelper
-from gobject import GObject, GType, PARAM_READWRITE
+from gobject import *
class PropertyObject(GObject):
__gproperties__ = {
'property': (str, 'blurb', 'description',
'default',
PARAM_READWRITE),
+ 'construct-property': (str, 'blurb', 'description',
+ 'default',
+ PARAM_READWRITE|PARAM_CONSTRUCT_ONLY),
}
def __init__(self):
GObject.__init__(self)
self._value = 'default'
+ self._construct_property = None
def do_get_property(self, pspec):
- if pspec.name != 'property':
- raise AssertionError
- return self._value
+ if pspec.name == 'property':
+ return self._value
+ elif pspec.name == 'construct-property':
+ return self._construct_property
+ raise AssertionError
+
def do_set_property(self, pspec, value):
- if pspec.name != 'property':
+ if pspec.name == 'property':
+ self._value = value
+ elif pspec.name == 'construct-property':
+ self._construct_property = value
+ else:
raise AssertionError
- self._value = value
+
class TestProperties(unittest.TestCase):
def testGetSet(self):
@@ -48,7 +59,13 @@ class TestProperties(unittest.TestCase):
for pspec in obj:
gtype = GType(pspec)
self.assertEqual(gtype.parent.name, 'GParam')
- self.assertEqual(pspec.name, 'property')
+ self.assert_(pspec.name in ['property', 'construct-property'])
+
+ self.assertEqual(len(obj), 2)
- self.assertEqual(len(obj), 1)
- self.assertEqual(list(obj), [pspec])
+ def testConstructProperty(self):
+ obj = new(PropertyObject, construct_property="123")
+ self.assertEqual(obj.props.construct_property, "123")
+ ## TODO: raise exception when setting construct-only properties
+ #obj.set_property("construct-property", "456")
+ #self.assertEqual(obj.props.construct_property, "456")
diff --git a/tests/test_subtype.py b/tests/test_subtype.py
index b2b5dcc..ea66fab 100644
--- a/tests/test_subtype.py
+++ b/tests/test_subtype.py
@@ -1,6 +1,7 @@
import unittest
from common import gobject, gtk, testhelper
+import testmodule
class TestSubType(unittest.TestCase):
def testSubType(self):
@@ -20,3 +21,35 @@ class TestSubType(unittest.TestCase):
treemodel = testhelper.get_tp_basicsize(gtk.TreeModel)
self.assert_(iface == treemodel)
+
+ def testBuiltinContructorRefcount(self):
+ foo = gtk.Label()
+ self.assertEqual(foo.__grefcount__, 1)
+
+ def testPyContructorRefcount(self):
+ foo = testmodule.PyLabel()
+ self.assertEqual(foo.__grefcount__, 1)
+
+ def testBuiltinObjNewRefcount(self):
+ foo = gobject.new(gtk.Label)
+ self.assertEqual(foo.__grefcount__, 1)
+
+ def testPyObjNewRefcount(self):
+ foo = gobject.new(testmodule.PyLabel)
+ self.assertEqual(foo.__grefcount__, 1)
+
+ def testPyContructorPropertyChaining(self):
+ foo = testmodule.PyLabel()
+ self.assertEqual(foo.__grefcount__, 1)
+
+ def testPyObjNewPropertyChaining(self):
+ foo = gobject.new(testmodule.PyLabel)
+ self.assertEqual(foo.props.label, "hello")
+
+ def testCPyCSubclassing1(self):
+ obj = testhelper.create_test_type()
+ self.assertEqual(obj.__grefcount__, 1)
+
+ def testCPyCSubclassing1(self):
+ refcount = testhelper.test_g_object_new()
+ self.assertEqual(refcount, 2)
diff --git a/tests/testhelpermodule.c b/tests/testhelpermodule.c
index cb9a991..962a0dc 100644
--- a/tests/testhelpermodule.c
+++ b/tests/testhelpermodule.c
@@ -5,6 +5,56 @@
#include "test-unknown.h"
+static GType
+py_label_get_type(void)
+{
+ static GType gtype = 0;
+ if (gtype == 0) {
+ PyObject *module;
+ if ((module = PyImport_ImportModule("testmodule")) != NULL) {
+ PyObject *moddict = PyModule_GetDict(module);
+ PyObject *py_label_type = PyDict_GetItemString(moddict, "PyLabel");
+ if (py_label_type != NULL)
+ gtype = pyg_type_from_object(py_label_type);
+ }
+ }
+ if (gtype == 0)
+ g_warning("could not get PyLabel from testmodule");
+ return gtype;
+}
+
+GType
+test_type_get_type(void)
+{
+ static GType gtype = 0;
+
+ if (gtype == 0)
+ {
+ GTypeQuery q;
+ GTypeInfo type_info = {
+ 0, /* class_size */
+
+ (GBaseInitFunc) NULL,
+ (GBaseFinalizeFunc) NULL,
+
+ (GClassInitFunc) NULL,
+ (GClassFinalizeFunc) NULL,
+ NULL, /* class_data */
+
+ 0, /* instance_size */
+ 0, /* n_preallocs */
+ (GInstanceInitFunc) NULL
+ };
+ g_type_query(py_label_get_type(), &q);
+ type_info.class_size = q.class_size;
+ type_info.instance_size = q.instance_size;
+ gtype = g_type_register_static(py_label_get_type(), "TestType", &type_info, 0);
+ }
+ return gtype;
+}
+
+#define TYPE_TEST (test_type_get_type())
+
static PyObject *
_wrap_get_tp_basicsize (PyObject * self, PyObject * args)
{
@@ -34,10 +84,35 @@ _wrap_get_unknown (PyObject * self)
}
+static PyObject *
+_wrap_create_test_type (PyObject * self)
+{
+ GObject *obj;
+ PyObject *rv;
+ obj = g_object_new(TYPE_TEST, NULL);
+ rv = pygobject_new(obj);
+ g_object_unref(obj);
+ return rv;
+}
+
+static PyObject *
+_wrap_test_g_object_new (PyObject * self)
+{
+ GObject *obj;
+ PyObject *rv;
+
+ obj = g_object_new(py_label_get_type(), NULL);
+ rv = PyInt_FromLong(obj->ref_count); /* should be == 2 at this point */
+ g_object_unref(obj);
+ return rv;
+}
+
static PyMethodDef testhelper_methods[] = {
{ "get_tp_basicsize", _wrap_get_tp_basicsize, METH_VARARGS },
{ "get_test_thread", (PyCFunction)_wrap_get_test_thread, METH_NOARGS },
{ "get_unknown", (PyCFunction)_wrap_get_unknown, METH_NOARGS },
+ { "create_test_type", (PyCFunction)_wrap_create_test_type, METH_NOARGS },
+ { "test_g_object_new", (PyCFunction)_wrap_test_g_object_new, METH_NOARGS },
{ NULL, NULL }
};
diff --git a/tests/testmodule.py b/tests/testmodule.py
new file mode 100644
index 0000000..8761ecf
--- /dev/null
+++ b/tests/testmodule.py
@@ -0,0 +1,9 @@
+import gobject
+import gtk
+
+class PyLabel(gtk.Label):
+ __gtype_name__ = 'PyLabel'
+
+ def __init__(self):
+ gtk.Label.__init__(self, "hello")
+