[pygobject] Replace GObject notify methods with introspection and python
- From: Simon Feltman <sfeltman src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [pygobject] Replace GObject notify methods with introspection and python
- Date: Wed, 7 Nov 2012 10:28:38 +0000 (UTC)
commit 463f660cd6bb78ae7f2ea7c70c0491e6b4744942
Author: Simon Feltman <sfeltman src gnome org>
Date: Tue Nov 6 21:34:06 2012 -0800
Replace GObject notify methods with introspection and python
Replace static context managers for freeze_notify and
handler_block with python context managers. Remove notify,
freeze_notify, thaw_notify static methods as the introspection
versions work fine.
https://bugzilla.gnome.org/show_bug.cgi?id=672727
gi/_gobject/pygobject.c | 173 +---------------------------------------------
gi/overrides/GObject.py | 61 +++++++++++++++--
tests/test_gobject.py | 26 +++++--
tests/test_signal.py | 13 +++-
4 files changed, 91 insertions(+), 182 deletions(-)
---
diff --git a/gi/_gobject/pygobject.c b/gi/_gobject/pygobject.c
index 23a4284..3d0c819 100644
--- a/gi/_gobject/pygobject.c
+++ b/gi/_gobject/pygobject.c
@@ -1055,115 +1055,6 @@ pygobject_watch_closure(PyObject *self, GClosure *closure)
}
-/* -------------- Freeze Notify Context Manager ----------------- */
-
-/**
- * pygcontext_manager_enter
- * @self: Freeze or Block context instance
- *
- * Method used for __enter__ on both GContextFeezeNotify and
- * GContextHandlerBlock. Does nothing since this is an object returned
- * by the freeze_notify() and handler_block() methods which do the actual
- * work of freezing and blocking.
- */
-static PyObject *
-pygcontext_manager_enter(PyObject *self)
-{
- Py_INCREF(self);
- return self;
-}
-
-typedef struct {
- PyObject_HEAD
- GObject *obj;
-} PyGContextFreezeNotify;
-
-PYGLIB_DEFINE_TYPE("gi._gobject.GContextFreezeNotify",
- PyGContextFreezeNotify_Type, PyGContextFreezeNotify);
-
-static PyObject *
-pygcontext_freeze_notify_new(GObject *gobj)
-{
- PyGContextFreezeNotify *context;
-
- context = PyObject_New(PyGContextFreezeNotify, &PyGContextFreezeNotify_Type);
- if (context == NULL)
- return NULL;
-
- g_object_ref(gobj);
- context->obj = gobj;
- return (PyObject*)context;
-}
-
-static void
-pygcontext_freeze_notify_dealloc(PyGContextFreezeNotify* self)
-{
- g_object_unref(self->obj);
- self->obj = NULL;
- PyObject_Del((PyObject*)self);
-}
-
-static PyObject *
-pygcontext_freeze_notify_exit(PyGContextFreezeNotify *self, PyObject *args)
-{
- g_object_thaw_notify(self->obj);
- Py_RETURN_NONE;
-}
-
-static PyMethodDef pygcontext_freeze_notify_methods[] = {
- {"__enter__", (PyCFunction)pygcontext_manager_enter, METH_NOARGS, ""},
- {"__exit__", (PyCFunction)pygcontext_freeze_notify_exit, METH_VARARGS, ""},
- {NULL}
-};
-
-/* -------------- Handler Block Context Manager ----------------- */
-typedef struct {
- PyObject_HEAD
- GObject *obj;
- gulong handler_id;
-} PyGContextHandlerBlock;
-
-PYGLIB_DEFINE_TYPE("gi._gobject.GContextHandlerBlock",
- PyGContextHandlerBlock_Type, PyGContextHandlerBlock);
-
-static PyObject *
-pygcontext_handler_block_new(GObject *gobj, gulong handler_id)
-{
- PyGContextHandlerBlock *context;
-
- context = PyObject_New(PyGContextHandlerBlock, &PyGContextHandlerBlock_Type);
- if (context == NULL)
- return NULL;
-
- g_object_ref(gobj);
- context->obj = gobj;
- context->handler_id = handler_id;
- return (PyObject*)context;
-}
-
-static void
-pygcontext_handler_block_dealloc(PyGContextHandlerBlock* self)
-{
- g_object_unref(self->obj);
- self->obj = NULL;
- PyObject_Del((PyObject*)self);
-}
-
-static PyObject *
-pygcontext_handler_block_exit(PyGContextHandlerBlock *self, PyObject *args)
-{
- g_signal_handler_unblock(self->obj, self->handler_id);
- Py_RETURN_NONE;
-}
-
-
-static PyMethodDef pygcontext_handler_block_methods[] = {
- {"__enter__", (PyCFunction)pygcontext_manager_enter, METH_NOARGS, ""},
- {"__exit__", (PyCFunction)pygcontext_handler_block_exit, METH_VARARGS, ""},
- {NULL}
-};
-
-
/* -------------- PyGObject behaviour ----------------- */
PYGLIB_DEFINE_TYPE("gi._gobject.GObject", PyGObject_Type, PyGObject);
@@ -1705,46 +1596,6 @@ pygobject_bind_property(PyGObject *self, PyObject *args)
}
static PyObject *
-pygobject_freeze_notify(PyGObject *self, PyObject *args)
-{
- if (!PyArg_ParseTuple(args, ":GObject.freeze_notify"))
- return NULL;
-
- CHECK_GOBJECT(self);
-
- g_object_freeze_notify(self->obj);
- return pygcontext_freeze_notify_new(self->obj);
-}
-
-static PyObject *
-pygobject_notify(PyGObject *self, PyObject *args)
-{
- char *property_name;
-
- if (!PyArg_ParseTuple(args, "s:GObject.notify", &property_name))
- return NULL;
-
- CHECK_GOBJECT(self);
-
- g_object_notify(self->obj, property_name);
- Py_INCREF(Py_None);
- return Py_None;
-}
-
-static PyObject *
-pygobject_thaw_notify(PyGObject *self, PyObject *args)
-{
- if (!PyArg_ParseTuple(args, ":GObject.thaw_notify"))
- return NULL;
-
- CHECK_GOBJECT(self);
-
- g_object_thaw_notify(self->obj);
- Py_INCREF(Py_None);
- return Py_None;
-}
-
-static PyObject *
pygobject_connect(PyGObject *self, PyObject *args)
{
PyObject *first, *callback, *extra_args, *repr = NULL;
@@ -1998,11 +1849,12 @@ pygobject_handler_block(PyGObject *self, PyObject *args)
if (!PyArg_ParseTuple(args, "k:GObject.handler_block", &handler_id))
return NULL;
-
+
CHECK_GOBJECT(self);
-
+
g_signal_handler_block(self->obj, handler_id);
- return pygcontext_handler_block_new(self->obj, handler_id);
+ Py_INCREF(Py_None);
+ return Py_None;
}
static PyObject *
@@ -2363,9 +2215,6 @@ static PyMethodDef pygobject_methods[] = {
{ "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 },
- { "freeze_notify", (PyCFunction)pygobject_freeze_notify, METH_VARARGS },
- { "notify", (PyCFunction)pygobject_notify, METH_VARARGS },
- { "thaw_notify", (PyCFunction)pygobject_thaw_notify, METH_VARARGS },
{ "connect", (PyCFunction)pygobject_connect, METH_VARARGS },
{ "connect_after", (PyCFunction)pygobject_connect_after, METH_VARARGS },
{ "connect_object", (PyCFunction)pygobject_connect_object, METH_VARARGS },
@@ -2722,18 +2571,4 @@ pygobject_object_register_types(PyObject *d)
if (PyType_Ready(&PyGBindingWeakRef_Type) < 0)
return;
PyDict_SetItemString(d, "GBindingWeakRef", (PyObject *) &PyGBindingWeakRef_Type);
-
- PyGContextFreezeNotify_Type.tp_dealloc = (destructor)pygcontext_freeze_notify_dealloc;
- PyGContextFreezeNotify_Type.tp_flags = Py_TPFLAGS_DEFAULT;
- PyGContextFreezeNotify_Type.tp_doc = "Context manager for freeze/thaw of GObjects";
- PyGContextFreezeNotify_Type.tp_methods = pygcontext_freeze_notify_methods;
- if (PyType_Ready(&PyGContextFreezeNotify_Type) < 0)
- return;
-
- PyGContextHandlerBlock_Type.tp_dealloc = (destructor)pygcontext_handler_block_dealloc;
- PyGContextHandlerBlock_Type.tp_flags = Py_TPFLAGS_DEFAULT;
- PyGContextHandlerBlock_Type.tp_doc = "Context manager for handler blocking of GObjects";
- PyGContextHandlerBlock_Type.tp_methods = pygcontext_handler_block_methods;
- if (PyType_Ready(&PyGContextHandlerBlock_Type) < 0)
- return;
}
diff --git a/gi/overrides/GObject.py b/gi/overrides/GObject.py
index 9435ba8..1827433 100644
--- a/gi/overrides/GObject.py
+++ b/gi/overrides/GObject.py
@@ -279,6 +279,29 @@ def signal_query(id_or_name, type_=None):
__all__.append('signal_query')
+class _HandlerBlockManager(object):
+ def __init__(self, obj, handler_id):
+ self.obj = obj
+ self.handler_id = handler_id
+
+ def __enter__(self):
+ pass
+
+ def __exit__(self, exc_type, exc_value, traceback):
+ _gobject.GObject.handler_unblock(self.obj, self.handler_id)
+
+
+class _FreezeNotifyManager(object):
+ def __init__(self, obj):
+ self.obj = obj
+
+ def __enter__(self):
+ pass
+
+ def __exit__(self, exc_type, exc_value, traceback):
+ self.obj.thaw_notify()
+
+
class Object(GObjectModule.Object):
def _unsupported_method(self, *args, **kargs):
raise RuntimeError('This method is currently unsupported.')
@@ -321,9 +344,6 @@ class Object(GObjectModule.Object):
set_property = _gobject.GObject.set_property
set_properties = _gobject.GObject.set_properties
bind_property = _gobject.GObject.bind_property
- freeze_notify = _gobject.GObject.freeze_notify
- notify = _gobject.GObject.notify
- thaw_notify = _gobject.GObject.thaw_notify
connect = _gobject.GObject.connect
connect_after = _gobject.GObject.connect_after
connect_object = _gobject.GObject.connect_object
@@ -332,8 +352,6 @@ class Object(GObjectModule.Object):
disconnect_by_func = _gobject.GObject.disconnect_by_func
handler_disconnect = _gobject.GObject.handler_disconnect
handler_is_connected = _gobject.GObject.handler_is_connected
- handler_block = _gobject.GObject.handler_block
- handler_unblock = _gobject.GObject.handler_unblock
handler_block_by_func = _gobject.GObject.handler_block_by_func
handler_unblock_by_func = _gobject.GObject.handler_unblock_by_func
emit = _gobject.GObject.emit
@@ -344,6 +362,39 @@ class Object(GObjectModule.Object):
__copy__ = _gobject.GObject.__copy__
__deepcopy__ = _gobject.GObject.__deepcopy__
+ def handler_block(self, handler_id):
+ """Blocks the signal handler from being invoked until handler_unblock() is called.
+
+ Returns a context manager which optionally can be used to
+ automatically unblock the handler:
+
+ >>> with obj.handler_block(id):
+ >>> pass
+ """
+
+ # Note Object.handler_block is a static method specific to pygobject and not
+ # found in introspection. We need to continue using the static method
+ # until we figure out a technique to call the global signal_handler_block.
+ # But this requires a gpointer to the Object which we currently don't have
+ # access to in python.
+ _gobject.GObject.handler_block(self, handler_id)
+ return _HandlerBlockManager(self, handler_id)
+
+ def freeze_notify(self):
+ """Freezes the object's property-changed notification queue.
+
+ This will freeze the object so that "notify" signals are blocked until
+ the thaw_notify() method is called.
+
+ Returns a context manager which optionally can be used to
+ automatically thaw notifications:
+
+ >>> with obj.freeze_notify():
+ >>> pass
+ """
+ super(Object, self).freeze_notify()
+ return _FreezeNotifyManager(self)
+
Object = override(Object)
GObject = Object
diff --git a/tests/test_gobject.py b/tests/test_gobject.py
index 80d0f64..99f471b 100644
--- a/tests/test_gobject.py
+++ b/tests/test_gobject.py
@@ -271,20 +271,26 @@ class TestContextManagers(unittest.TestCase):
self.assertEqual(self.tracking, [1, 2])
self.assertEqual(self.obj.__grefcount__, 1)
- # Using the context manager the tracking list should not be affected
- # and the GObject reference count should go up.
+ pyref_count = sys.getrefcount(self.obj)
+
+ # Using the context manager the tracking list should not be affected.
+ # The GObject reference count should stay the same and the python
+ # object ref-count should go up.
with self.obj.freeze_notify():
- self.assertEqual(self.obj.__grefcount__, 2)
+ self.assertEqual(self.obj.__grefcount__, 1)
+ self.assertEqual(sys.getrefcount(self.obj), pyref_count + 1)
self.obj.props.prop = 3
self.assertEqual(self.obj.props.prop, 3)
self.assertEqual(self.tracking, [1, 2])
# After the context manager, the prop should have been modified,
- # the tracking list will be modified, and the GObject ref
+ # the tracking list will be modified, and the python object ref
# count goes back down.
+ gc.collect()
self.assertEqual(self.obj.props.prop, 3)
self.assertEqual(self.tracking, [1, 2, 3])
self.assertEqual(self.obj.__grefcount__, 1)
+ self.assertEqual(sys.getrefcount(self.obj), pyref_count)
def test_handler_block_context(self):
# Verify prop tracking list
@@ -295,10 +301,14 @@ class TestContextManagers(unittest.TestCase):
self.assertEqual(self.tracking, [1, 2])
self.assertEqual(self.obj.__grefcount__, 1)
- # Using the context manager the tracking list should not be affected
- # and the GObject reference count should go up.
+ pyref_count = sys.getrefcount(self.obj)
+
+ # Using the context manager the tracking list should not be affected.
+ # The GObject reference count should stay the same and the python
+ # object ref-count should go up.
with self.obj.handler_block(self.handler):
- self.assertEqual(self.obj.__grefcount__, 2)
+ self.assertEqual(self.obj.__grefcount__, 1)
+ self.assertEqual(sys.getrefcount(self.obj), pyref_count + 1)
self.obj.props.prop = 3
self.assertEqual(self.obj.props.prop, 3)
self.assertEqual(self.tracking, [1, 2])
@@ -306,9 +316,11 @@ class TestContextManagers(unittest.TestCase):
# After the context manager, the prop should have been modified
# the tracking list should have stayed the same and the GObject ref
# count goes back down.
+ gc.collect()
self.assertEqual(self.obj.props.prop, 3)
self.assertEqual(self.tracking, [1, 2])
self.assertEqual(self.obj.__grefcount__, 1)
+ self.assertEqual(sys.getrefcount(self.obj), pyref_count)
def test_freeze_notify_context_nested(self):
self.assertEqual(self.tracking, [])
diff --git a/tests/test_signal.py b/tests/test_signal.py
index ec58336..fb2cb24 100644
--- a/tests/test_signal.py
+++ b/tests/test_signal.py
@@ -596,7 +596,8 @@ class TestSignalDecorator(unittest.TestCase):
class TestSignalConnectors(unittest.TestCase):
class CustomButton(GObject.GObject):
- value = 0
+ on_notify_called = False
+ value = GObject.Property(type=int)
@GObject.Signal(arg_types=(int,))
def clicked(self, value):
@@ -610,6 +611,16 @@ class TestSignalConnectors(unittest.TestCase):
self.obj = obj
self.value = value
+ def test_signal_notify(self):
+ def on_notify(obj, param):
+ obj.on_notify_called = True
+
+ obj = self.CustomButton()
+ obj.connect('notify', on_notify)
+ self.assertFalse(obj.on_notify_called)
+ obj.notify('value')
+ self.assertTrue(obj.on_notify_called)
+
def test_signal_emit(self):
# standard callback connection with different forms of emit.
obj = self.CustomButton()
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]