[pygobject] Remove static child_add_watch() binding



commit 70d78eee4a04dcaefea4615fe351e33fa717dffa
Author: Martin Pitt <martinpitt gnome org>
Date:   Sun Oct 28 14:15:05 2012 +0100

    Remove static child_add_watch() binding
    
    Use the GLib API through GI instead, and provide override to keep backwards
    compatible API. Also allow using the actual GLib API, and deprecate the old
    static API of calling without a priority as first argument.

 docs/reference/pyglib-functions.xml |   61 -------------------------
 gi/_glib/__init__.py                |    1 -
 gi/_glib/glibmodule.c               |   72 ------------------------------
 gi/_gobject/__init__.py             |    1 -
 gi/overrides/GLib.py                |   46 +++++++++++++++++++
 gi/overrides/GObject.py             |    2 +-
 tests/test_mainloop.py              |    2 +-
 tests/test_subprocess.py            |   84 ++++++++++++++++++++++++++++++++--
 8 files changed, 127 insertions(+), 142 deletions(-)
---
diff --git a/docs/reference/pyglib-functions.xml b/docs/reference/pyglib-functions.xml
index 936f750..fa1c210 100644
--- a/docs/reference/pyglib-functions.xml
+++ b/docs/reference/pyglib-functions.xml
@@ -13,12 +13,6 @@
 
     <programlisting>
 <methodsynopsis language="python">
-        <methodname><link linkend="function-glib--child-watch-add">glib.child_watch_add</link></methodname>
-        <methodparam><parameter role="keyword">pid</parameter></methodparam>
-        <methodparam><parameter role="keyword">function</parameter></methodparam>
-        <methodparam><parameter role="keyword">data</parameter><initializer>None</initializer></methodparam>
-        <methodparam><parameter role="keyword">priority</parameter><initializer>glib.PRIORITY_DEFAULT</initializer></methodparam>
-      </methodsynopsis><methodsynopsis language="python">
 	<methodname><link
 linkend="function-glib--spawn-async">glib.spawn_async</link></methodname>
 	<methodparam><parameter role="keyword">argv</parameter></methodparam>
@@ -91,61 +85,6 @@ line endings and attribute values.</para>
 
     </refsect2>
 
-    <refsect2 id="function-glib--child-watch-add">
-      <title>glib.child_watch_add</title>
-      
-      <programlisting><methodsynopsis language="python">
-        <methodname>glib.child_watch_add</methodname>
-        <methodparam><parameter role="keyword">pid</parameter></methodparam>
-        <methodparam><parameter role="keyword">function</parameter></methodparam>
-        <methodparam><parameter role="keyword">data</parameter><initializer>None</initializer></methodparam>
-        <methodparam><parameter role="keyword">priority</parameter><initializer>glib.PRIORITY_DEFAULT</initializer></methodparam>
-	</methodsynopsis></programlisting>
-      <variablelist role="params">
-        <varlistentry>
-          <term><parameter role="keyword">pid</parameter>&nbsp;:</term>
-          <listitem><simpara>process id of a child process to watch</simpara></listitem>
-        </varlistentry>
-        <varlistentry><term><parameter role="keyword">function</parameter>&nbsp;:</term>
-          <listitem><simpara>the function to call</simpara></listitem>
-        </varlistentry>
-        <varlistentry>
-          <term><parameter role="keyword">data</parameter>&nbsp;:</term>
-          <listitem><simpara>the optional data to pass to
-<parameter>function</parameter></simpara></listitem>
-        </varlistentry>
-         <varlistentry>
-          <term><parameter role="keyword">priority</parameter>&nbsp;:</term>
-          <listitem><simpara>the priority of the idle source - one of the
-<xref linkend="glib-priority-constants"
-endterm="glib-priority-constants-title"></xref></simpara></listitem>
-        </varlistentry>
-        <varlistentry>
-          <term><emphasis>Returns</emphasis>&nbsp;:</term>
-          <listitem><simpara>the id of event source.</simpara></listitem>
-        </varlistentry>
-      </variablelist>
-      <note>
-        <para>This function is available in PyGTK 2.6 and above.</para>
-      </note>
-
-      <para>The <function>glib.child_watch_add</function>() function sets
-the function specified by <parameter>function</parameter> to be called with
-the user data specified by <parameter>data</parameter> when the child
-indicated by <parameter>pid</parameter> exits. The signature for the
-callback is:</para>
-
-      <programlisting>
-def callback(pid, condition, user_data)
-</programlisting>
-
-      <para>where <parameter>pid</parameter> is is the child process id,
-<parameter>condition</parameter> is the status information about the child
-process and <parameter>user_data</parameter> is <parameter>data</parameter>
-PyGTK supports only a single callback per process id.</para>
-
-    </refsect2>
-
     <refsect2 id="function-glib--spawn-async">
       <title>glib.spawn_async</title>
       
diff --git a/gi/_glib/__init__.py b/gi/_glib/__init__.py
index 0d6d302..a9fee83 100644
--- a/gi/_glib/__init__.py
+++ b/gi/_glib/__init__.py
@@ -45,7 +45,6 @@ OPTION_FLAG_REVERSE = _glib.OPTION_FLAG_REVERSE
 OPTION_REMAINING = _glib.OPTION_REMAINING
 
 # Functions
-child_watch_add = _glib.child_watch_add
 filename_from_utf8 = _glib.filename_from_utf8
 get_current_time = _glib.get_current_time
 glib_version = _glib.glib_version
diff --git a/gi/_glib/glibmodule.c b/gi/_glib/glibmodule.c
index 4e20444..21cd52c 100644
--- a/gi/_glib/glibmodule.c
+++ b/gi/_glib/glibmodule.c
@@ -96,69 +96,6 @@ pyglib_threads_init(PyObject *unused, PyObject *args, PyObject *kwargs)
     return Py_None;
 }
 
-static void
-child_watch_func(GPid pid, gint status, gpointer data)
-{
-    struct _PyGChildData *child_data = (struct _PyGChildData *) data;
-    PyObject *retval;
-    PyGILState_STATE gil;
-
-    gil = pyglib_gil_state_ensure();
-    if (child_data->data)
-        retval = PyObject_CallFunction(child_data->func, "iiO", pid, status,
-                                       child_data->data);
-    else
-        retval = PyObject_CallFunction(child_data->func, "ii", pid, status);
-
-    if (retval)
-	Py_DECREF(retval);
-    else
-	PyErr_Print();
-
-    pyglib_gil_state_release(gil);
-}
-
-static void
-child_watch_dnotify(gpointer data)
-{
-    struct _PyGChildData *child_data = (struct _PyGChildData *) data;
-    Py_DECREF(child_data->func);
-    Py_XDECREF(child_data->data);
-    g_slice_free(struct _PyGChildData, child_data);
-}
-
-
-static PyObject *
-pyglib_child_watch_add(PyObject *unused, PyObject *args, PyObject *kwargs)
-{
-    static char *kwlist[] = { "pid", "function", "data", "priority", NULL };
-    guint id;
-    gint priority = G_PRIORITY_DEFAULT;
-    int pid;
-    PyObject *func, *user_data = NULL;
-    struct _PyGChildData *child_data;
-
-    if (!PyArg_ParseTupleAndKeywords(args, kwargs,
-				     "iO|Oi:glib.child_watch_add", kwlist,
-                                     &pid, &func, &user_data, &priority))
-        return NULL;
-    if (!PyCallable_Check(func)) {
-        PyErr_SetString(PyExc_TypeError,
-                        "_glib.child_watch_add: second argument must be callable");
-        return NULL;
-    }
-
-    child_data = g_slice_new(struct _PyGChildData);
-    child_data->func = func;
-    child_data->data = user_data;
-    Py_INCREF(child_data->func);
-    if (child_data->data)
-        Py_INCREF(child_data->data);
-    id = g_child_watch_add_full(priority, pid, child_watch_func,
-                                child_data, child_watch_dnotify);
-    return PYGLIB_PyLong_FromLong(id);
-}
-
 static PyObject *
 pyglib_get_current_time(PyObject *unused)
 {
@@ -199,15 +136,6 @@ static PyMethodDef _glib_functions[] = {
       "Initialize GLib for use from multiple threads. If you also use GTK+\n"
       "itself (i.e. GUI, not just PyGObject), use gtk.gdk.threads_init()\n"
       "instead." },
-    { "child_watch_add",
-      (PyCFunction)pyglib_child_watch_add, METH_VARARGS|METH_KEYWORDS,
-      "child_watch_add(pid, callable, user_data=None,\n"
-                       "priority=None) -> source id\n"
-      "  callable receives (pid, condition, user_data)\n"
-      "Sets the function specified by function to be called with the user\n"
-      "data specified by data when the child indicated by pid exits.\n"
-      "Condition is a combination of glib.IO_IN, glib.IO_OUT, glib.IO_PRI,\n"
-      "gio.IO_ERR and gio.IO_HUB." },
     { "spawn_async",
       (PyCFunction)pyglib_spawn_async, METH_VARARGS|METH_KEYWORDS,
       "spawn_async(argv, envp=None, working_directory=None,\n"
diff --git a/gi/_gobject/__init__.py b/gi/_gobject/__init__.py
index c774df6..b0da863 100644
--- a/gi/_gobject/__init__.py
+++ b/gi/_gobject/__init__.py
@@ -82,7 +82,6 @@ type_parent = _gobject.type_parent
 type_register = _gobject.type_register
 
 spawn_async = _glib.spawn_async
-child_watch_add = _glib.child_watch_add
 get_current_time = _glib.get_current_time
 filename_from_utf8 = _glib.filename_from_utf8
 Pid = _glib.Pid
diff --git a/gi/overrides/GLib.py b/gi/overrides/GLib.py
index 794f3e6..a03ed2a 100644
--- a/gi/overrides/GLib.py
+++ b/gi/overrides/GLib.py
@@ -715,6 +715,52 @@ IOChannel = override(IOChannel)
 __all__.append('IOChannel')
 
 
+# The real GLib API is child_watch_add(priority, pid, callback, data).
+# The old static bindings had the following API which we still need to support
+# for a while:
+#   child_watch_add(pid, callback, data=None, priority=GLib.PRIORITY_DEFAULT)
+# and the usual "call without user_data", in which case the callback does not
+# get an user_data either.
+def child_watch_add(priority_or_pid, pid_or_callback, *args, **kwargs):
+    if callable(pid_or_callback):
+        warnings.warn('Calling child_watch_add without priority as first argument is deprecated',
+                      PyGIDeprecationWarning)
+        pid = priority_or_pid
+        callback = pid_or_callback
+        if len(args) == 0:
+            user_data = kwargs.get('data', _unspecified)
+            priority = kwargs.get('priority', GLib.PRIORITY_DEFAULT)
+        elif len(args) == 1:
+            user_data = args[0]
+            priority = kwargs.get('priority', GLib.PRIORITY_DEFAULT)
+        elif len(args) == 2:
+            user_data = args[0]
+            priority = args[1]
+        else:
+            raise TypeError('expected at most 4 positional arguments')
+    else:
+        priority = priority_or_pid
+        pid = pid_or_callback
+        if len(args) == 0 or not callable(args[0]):
+            raise TypeError('expected callback as third argument')
+        callback = args[0]
+        if len(args) == 1:
+            user_data = kwargs.get('data', _unspecified)
+        else:
+            user_data = args[1]
+
+    if user_data is _unspecified:
+        # we have to call the callback without the user_data argument
+        func = lambda pid, status, data: callback(pid, status)
+        user_data = None
+    else:
+        func = callback
+
+    return GLib.child_watch_add(priority, pid, func, user_data)
+
+__all__.append('child_watch_add')
+
+
 # work around wrong constants in GLib GIR, see
 # https://bugzilla.gnome.org/show_bug.cgi?id=685022
 MININT64 = -9223372036854775808
diff --git a/gi/overrides/GObject.py b/gi/overrides/GObject.py
index 7925dab..d7d8ade 100644
--- a/gi/overrides/GObject.py
+++ b/gi/overrides/GObject.py
@@ -32,7 +32,7 @@ for name in ['markup_escape_text', 'get_application_name',
              'MainLoop', 'MainContext', 'main_context_default',
              'source_remove', 'Source', 'Idle', 'Timeout', 'PollFD',
              'idle_add', 'timeout_add', 'timeout_add_seconds',
-             'io_add_watch']:
+             'io_add_watch', 'child_watch_add']:
     globals()[name] = gi.overrides.deprecated(getattr(GLib, name), 'GLib.' + name)
     __all__.append(name)
 
diff --git a/tests/test_mainloop.py b/tests/test_mainloop.py
index 3c75342..44197b3 100644
--- a/tests/test_mainloop.py
+++ b/tests/test_mainloop.py
@@ -34,7 +34,7 @@ class TestMainLoop(unittest.TestCase):
             raise Exception("deadbabe")
 
         loop = GLib.MainLoop()
-        GLib.child_watch_add(pid, child_died, loop)
+        GLib.child_watch_add(GLib.PRIORITY_DEFAULT, pid, child_died, loop)
 
         os.close(pipe_r)
         os.write(pipe_w, _bytes("Y"))
diff --git a/tests/test_subprocess.py b/tests/test_subprocess.py
index 65d9eeb..f82c53b 100644
--- a/tests/test_subprocess.py
+++ b/tests/test_subprocess.py
@@ -2,40 +2,114 @@
 
 import sys
 import unittest
+import warnings
 
 from gi.repository import GLib
+from gi import PyGIDeprecationWarning
 
 
 class TestProcess(unittest.TestCase):
 
+    def test_deprecated_child_watch_no_data(self):
+        def cb(pid, status):
+            self.status = status
+            self.loop.quit()
+
+        self.status = None
+        self.loop = GLib.MainLoop()
+        argv = [sys.executable, '-c', 'import sys']
+        pid, stdin, stdout, stderr = GLib.spawn_async(
+            argv, flags=GLib.SpawnFlags.DO_NOT_REAP_CHILD)
+        pid.close()
+        with warnings.catch_warnings(record=True) as w:
+            warnings.simplefilter('always')
+            GLib.child_watch_add(pid, cb)
+            self.assertTrue(issubclass(w[0].category, PyGIDeprecationWarning))
+        self.loop.run()
+        self.assertEqual(self.status, 0)
+
+    def test_deprecated_child_watch_data_priority(self):
+        def cb(pid, status, data):
+            self.data = data
+            self.status = status
+            self.loop.quit()
+
+        self.status = None
+        self.data = None
+        self.loop = GLib.MainLoop()
+        argv = [sys.executable, '-c', 'import sys']
+        pid, stdin, stdout, stderr = GLib.spawn_async(
+            argv, flags=GLib.SpawnFlags.DO_NOT_REAP_CHILD)
+        pid.close()
+        with warnings.catch_warnings(record=True) as w:
+            warnings.simplefilter('always')
+            id = GLib.child_watch_add(pid, cb, 12345, GLib.PRIORITY_HIGH)
+            self.assertTrue(issubclass(w[0].category, PyGIDeprecationWarning))
+        self.assertEqual(self.loop.get_context().find_source_by_id(id).priority,
+                         GLib.PRIORITY_HIGH)
+        self.loop.run()
+        self.assertEqual(self.data, 12345)
+        self.assertEqual(self.status, 0)
+
+    def test_deprecated_child_watch_data_priority_kwargs(self):
+        def cb(pid, status, data):
+            self.data = data
+            self.status = status
+            self.loop.quit()
+
+        self.status = None
+        self.data = None
+        self.loop = GLib.MainLoop()
+        argv = [sys.executable, '-c', 'import sys']
+        pid, stdin, stdout, stderr = GLib.spawn_async(
+            argv, flags=GLib.SpawnFlags.DO_NOT_REAP_CHILD)
+        pid.close()
+        with warnings.catch_warnings(record=True) as w:
+            warnings.simplefilter('always')
+            id = GLib.child_watch_add(pid, cb, priority=GLib.PRIORITY_HIGH, data=12345)
+            self.assertTrue(issubclass(w[0].category, PyGIDeprecationWarning))
+        self.assertEqual(self.loop.get_context().find_source_by_id(id).priority,
+                         GLib.PRIORITY_HIGH)
+        self.loop.run()
+        self.assertEqual(self.data, 12345)
+        self.assertEqual(self.status, 0)
+
     def test_child_watch_no_data(self):
-        def cb(pid, condition):
+        def cb(pid, status):
+            self.status = status
             self.loop.quit()
 
+        self.status = None
         self.loop = GLib.MainLoop()
         argv = [sys.executable, '-c', 'import sys']
         pid, stdin, stdout, stderr = GLib.spawn_async(
             argv, flags=GLib.SpawnFlags.DO_NOT_REAP_CHILD)
         pid.close()
-        GLib.child_watch_add(pid, cb)
+        id = GLib.child_watch_add(GLib.PRIORITY_HIGH, pid, cb)
+        self.assertEqual(self.loop.get_context().find_source_by_id(id).priority,
+                         GLib.PRIORITY_HIGH)
         self.loop.run()
+        self.assertEqual(self.status, 0)
 
-    def test_child_watch_data_priority(self):
-        def cb(pid, condition, data):
+    def test_child_watch_with_data(self):
+        def cb(pid, status, data):
+            self.status = status
             self.data = data
             self.loop.quit()
 
         self.data = None
+        self.status = None
         self.loop = GLib.MainLoop()
         argv = [sys.executable, '-c', 'import sys']
         pid, stdin, stdout, stderr = GLib.spawn_async(
             argv, flags=GLib.SpawnFlags.DO_NOT_REAP_CHILD)
         pid.close()
-        id = GLib.child_watch_add(pid, cb, 12345, GLib.PRIORITY_HIGH)
+        id = GLib.child_watch_add(GLib.PRIORITY_HIGH, pid, cb, 12345)
         self.assertEqual(self.loop.get_context().find_source_by_id(id).priority,
                          GLib.PRIORITY_HIGH)
         self.loop.run()
         self.assertEqual(self.data, 12345)
+        self.assertEqual(self.status, 0)
 
     def test_backwards_compat_flags(self):
         self.assertEqual(GLib.SpawnFlags.DO_NOT_REAP_CHILD,



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]