[pygobject] Remove static GIOChannel bindings



commit 15e717ce2c2a26c02c913f79bc7cf6876d943e92
Author: Martin Pitt <martinpitt gnome org>
Date:   Thu Oct 25 15:55:46 2012 +0200

    Remove static GIOChannel bindings
    
    Use the GLib API through GI instead, and provide overrides to keep backwards
    compatible API, including its bugs.
    
    We still need to keep a static wrapper around g_io_channel_read_chars() until
    we teach PyGObject to correctly handle caller allocated out array arguments.
    
    https://bugzilla.gnome.org/show_bug.cgi?id=686795

 gi/_glib/Makefile.am    |    2 -
 gi/_glib/__init__.py    |    1 -
 gi/_glib/glibmodule.c   |    2 -
 gi/_glib/pygiochannel.c |  748 -----------------------------------------------
 gi/_glib/pygiochannel.h |   29 --
 gi/_gobject/__init__.py |    1 -
 gi/gimodule.c           |   72 +++++
 gi/overrides/GLib.py    |   95 ++++++-
 8 files changed, 161 insertions(+), 789 deletions(-)
---
diff --git a/gi/_glib/Makefile.am b/gi/_glib/Makefile.am
index fed1b43..1f004a2 100644
--- a/gi/_glib/Makefile.am
+++ b/gi/_glib/Makefile.am
@@ -52,8 +52,6 @@ pyglib_LTLIBRARIES = _glib.la
 
 _glib_la_SOURCES = \
 	glibmodule.c \
-	pygiochannel.c \
-	pygiochannel.h \
 	pygoptioncontext.c \
 	pygoptioncontext.h \
 	pygoptiongroup.c \
diff --git a/gi/_glib/__init__.py b/gi/_glib/__init__.py
index 145685e..7844c12 100644
--- a/gi/_glib/__init__.py
+++ b/gi/_glib/__init__.py
@@ -26,7 +26,6 @@ _PyGLib_API = _glib._PyGLib_API
 
 # Types
 GError = _glib.GError
-IOChannel = _glib.IOChannel
 OptionContext = _glib.OptionContext
 OptionGroup = _glib.OptionGroup
 Pid = _glib.Pid
diff --git a/gi/_glib/glibmodule.c b/gi/_glib/glibmodule.c
index 1513748..40c9638 100644
--- a/gi/_glib/glibmodule.c
+++ b/gi/_glib/glibmodule.c
@@ -29,7 +29,6 @@
 #include <glib.h>
 #include "pyglib.h"
 #include "pyglib-private.h"
-#include "pygiochannel.h"
 #include "pygoptioncontext.h"
 #include "pygoptiongroup.h"
 #include "pygsource.h"
@@ -424,7 +423,6 @@ PYGLIB_MODULE_START(_glib, "_glib")
     pyglib_register_api(d);
     pyglib_register_error(d);
     pyglib_register_version_tuples(d);
-    pyglib_iochannel_register_types(d);
     pyglib_source_register_types(d);
     pyglib_spawn_register_types(d);
     pyglib_option_context_register_types(d);
diff --git a/gi/_gobject/__init__.py b/gi/_gobject/__init__.py
index 50a130f..75f6643 100644
--- a/gi/_gobject/__init__.py
+++ b/gi/_gobject/__init__.py
@@ -89,7 +89,6 @@ filename_from_utf8 = _glib.filename_from_utf8
 Pid = _glib.Pid
 GError = _glib.GError
 glib_version = _glib.glib_version
-IOChannel = _glib.IOChannel
 OptionGroup = _glib.OptionGroup
 OptionContext = _glib.OptionContext
 
diff --git a/gi/gimodule.c b/gi/gimodule.c
index fcab468..76530f1 100644
--- a/gi/gimodule.c
+++ b/gi/gimodule.c
@@ -23,6 +23,7 @@
 
 #include "pygi-private.h"
 #include "pygi.h"
+#include "pyglib.h"
 
 #include <pygobject.h>
 #include <pyglib-python-compat.h>
@@ -461,6 +462,76 @@ _wrap_pyg_source_new (PyObject *self, PyObject *args)
     return pyg_source_new ();
 }
 
+#define CHUNK_SIZE 8192
+
+static PyObject*
+pyg_channel_read(PyObject* self, PyObject *args, PyObject *kwargs)
+{
+    int max_count = -1;
+    PyObject *py_iochannel, *ret_obj = NULL;
+    gsize total_read = 0;
+    GError* error = NULL;
+    GIOStatus status = G_IO_STATUS_NORMAL;
+
+    if (!PyArg_ParseTuple (args, "Oi:pyg_channel_read", &py_iochannel, &max_count)) {
+        return NULL;
+    }
+    if (!pyg_boxed_check (py_iochannel, G_TYPE_IO_CHANNEL)) {
+        PyErr_SetString(PyExc_TypeError, "first argument is not a GLib.IOChannel");
+        return NULL;
+    }
+	
+    if (max_count == 0)
+        return PYGLIB_PyBytes_FromString("");
+    
+    while (status == G_IO_STATUS_NORMAL
+	   && (max_count == -1 || total_read < max_count)) {
+	gsize single_read;
+	char* buf;
+	gsize buf_size;
+	
+	if (max_count == -1) 
+	    buf_size = CHUNK_SIZE;
+	else {
+	    buf_size = max_count - total_read;
+	    if (buf_size > CHUNK_SIZE)
+		buf_size = CHUNK_SIZE;
+        }
+	
+	if ( ret_obj == NULL ) {
+	    ret_obj = PYGLIB_PyBytes_FromStringAndSize((char *)NULL, buf_size);
+	    if (ret_obj == NULL)
+		goto failure;
+	}
+	else if (buf_size + total_read > PYGLIB_PyBytes_Size(ret_obj)) {
+	    if (PYGLIB_PyBytes_Resize(&ret_obj, buf_size + total_read) == -1)
+		goto failure;
+	}
+       
+        buf = PYGLIB_PyBytes_AsString(ret_obj) + total_read;
+
+        pyglib_unblock_threads();
+        status = g_io_channel_read_chars(pyg_boxed_get (py_iochannel, GIOChannel),
+                                         buf, buf_size, &single_read, &error);
+        pyglib_block_threads();
+	if (pyglib_error_check(&error))
+	    goto failure;
+	
+	total_read += single_read;
+    }
+	
+    if ( total_read != PYGLIB_PyBytes_Size(ret_obj) ) {
+	if (PYGLIB_PyBytes_Resize(&ret_obj, total_read) == -1)
+	    goto failure;
+    }
+
+    return ret_obj;
+
+  failure:
+    Py_XDECREF(ret_obj);
+    return NULL;
+}
+
 
 static PyMethodDef _gi_functions[] = {
     { "enum_add", (PyCFunction) _wrap_pyg_enum_add, METH_VARARGS | METH_KEYWORDS },
@@ -474,6 +545,7 @@ static PyMethodDef _gi_functions[] = {
     { "variant_type_from_string", (PyCFunction) _wrap_pyg_variant_type_from_string, METH_VARARGS },
     { "source_new", (PyCFunction) _wrap_pyg_source_new, METH_NOARGS },
     { "source_set_callback", (PyCFunction) pyg_source_set_callback, METH_VARARGS },
+    { "io_channel_read", (PyCFunction) pyg_channel_read, METH_VARARGS },
     { NULL, NULL, 0 }
 };
 
diff --git a/gi/overrides/GLib.py b/gi/overrides/GLib.py
index 22de6bf..996bc3d 100644
--- a/gi/overrides/GLib.py
+++ b/gi/overrides/GLib.py
@@ -22,7 +22,8 @@
 import signal
 
 from ..module import get_introspection_module
-from .._gi import variant_new_tuple, variant_type_from_string, source_new, source_set_callback
+from .._gi import (variant_new_tuple, variant_type_from_string, source_new,
+                   source_set_callback, io_channel_read)
 from ..overrides import override, deprecated
 
 GLib = get_introspection_module('GLib')
@@ -563,37 +564,119 @@ class Timeout(Source):
 __all__.append('Timeout')
 
 
-__unspecified = object()
+_unspecified = object()
 
 
 # backwards compatible API
 def _glib_idle_adjust_callback(function, user_data):
-    if user_data is __unspecified:
+    if user_data is _unspecified:
         # we have to call the callback without the user_data argument
         return (lambda data: function(), None)
     return (function, user_data)
 
 
-def idle_add(function, user_data=__unspecified, priority=GLib.PRIORITY_DEFAULT_IDLE):
+def idle_add(function, user_data=_unspecified, priority=GLib.PRIORITY_DEFAULT_IDLE):
     (function, user_data) = _glib_idle_adjust_callback(function, user_data)
     return GLib.idle_add(priority, function, user_data)
 
 __all__.append('idle_add')
 
 
-def timeout_add(interval, function, user_data=__unspecified, priority=GLib.PRIORITY_DEFAULT):
+def timeout_add(interval, function, user_data=_unspecified, priority=GLib.PRIORITY_DEFAULT):
     (function, user_data) = _glib_idle_adjust_callback(function, user_data)
     return GLib.timeout_add(priority, interval, function, user_data)
 
 __all__.append('timeout_add')
 
 
-def timeout_add_seconds(interval, function, user_data=__unspecified, priority=GLib.PRIORITY_DEFAULT):
+def timeout_add_seconds(interval, function, user_data=_unspecified, priority=GLib.PRIORITY_DEFAULT):
     (function, user_data) = _glib_idle_adjust_callback(function, user_data)
     return GLib.timeout_add_seconds(priority, interval, function, user_data)
 
 __all__.append('timeout_add_seconds')
 
+
+# backwards compatible API
+class IOChannel(GLib.IOChannel):
+    def __new__(cls, filedes=None, filename=None, mode=None, hwnd=None):
+        if filedes is not None:
+            return GLib.IOChannel.unix_new(filedes)
+        if filename is not None:
+            return GLib.IOChannel.new_file(filename, mode or 'r')
+        if hwnd is not None:
+            return GLib.IOChannel.win32_new_fd(hwnd)
+        raise TypeError('either a valid file descriptor, file name, or window handle must be supplied')
+
+    def read(self, max_count=-1):
+        return io_channel_read(self, max_count)
+
+    def readline(self, size_hint=-1):
+        # note, size_hint is just to maintain backwards compatible API; the
+        # old static binding did not actually use it
+        (status, buf, length, terminator_pos) = self.read_line()
+        if buf is None:
+            return ''
+        return buf
+
+    def readlines(self, size_hint=-1):
+        # note, size_hint is just to maintain backwards compatible API;
+        # the old static binding did not actually use it
+        lines = []
+        status = GLib.IOStatus.NORMAL
+        while status == GLib.IOStatus.NORMAL:
+            (status, buf, length, terminator_pos) = self.read_line()
+            # note, this appends an empty line after EOF; this is
+            # bug-compatible with the old static bindings
+            if buf is None:
+                buf = ''
+            lines.append(buf)
+        return lines
+
+    def write(self, buf, buflen=-1):
+        if not isinstance(buf, bytes):
+            buf = buf.encode('UTF-8')
+        if buflen == -1:
+            buflen = len(buf)
+        (status, written) = self.write_chars(buf, buflen)
+        return written
+
+    def writelines(self, lines):
+        for line in lines:
+            self.write(line)
+
+    _whence_map = {0: GLib.SeekType.SET, 1: GLib.SeekType.CUR, 2: GLib.SeekType.END}
+
+    def seek(self, offset, whence=0):
+        try:
+            w = self._whence_map[whence]
+        except KeyError:
+            raise ValueError("invalid 'whence' value")
+        return self.seek_position(offset, w)
+
+    def add_watch(self, condition, callback, user_data=_unspecified,
+                  priority=GLib.PRIORITY_DEFAULT):
+        if user_data is _unspecified:
+            # we have to call the callback without the user_data argument
+            func = lambda channel, cond, data: callback(channel, cond)
+            user_data = None
+        else:
+            func = callback
+        return GLib.io_add_watch(self, priority, condition, func, user_data)
+
+    def __iter__(self):
+        return self
+
+    def __next__(self):
+        (status, buf, length, terminator_pos) = self.read_line()
+        if status == GLib.IOStatus.NORMAL:
+            return buf
+        raise StopIteration
+
+
+IOChannel = override(IOChannel)
+__all__.append('IOChannel')
+
+
 # work around wrong constants in GLib GIR, see
 # https://bugzilla.gnome.org/show_bug.cgi?id=685022
 MININT64 = -9223372036854775808



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