[gedit/wip/etfixes: 2/2] external tools: Use a GIOchannel also for stdin



commit bc93b440b6479b32419f1eec44bf974f0ee8a6d6
Author: Paolo Borelli <pborelli gnome org>
Date:   Sat Mar 29 15:37:20 2014 +0100

    external tools: Use a GIOchannel also for stdin
    
    Write into the stding pipe asynchronously using a GIOChannel in
    nonblocking mode

 plugins/externaltools/tools/capture.py |  111 +++++++++++++++++--------------
 1 files changed, 61 insertions(+), 50 deletions(-)
---
diff --git a/plugins/externaltools/tools/capture.py b/plugins/externaltools/tools/capture.py
index 488d6ee..b5c0f4e 100644
--- a/plugins/externaltools/tools/capture.py
+++ b/plugins/externaltools/tools/capture.py
@@ -61,7 +61,7 @@ class Capture(GObject.Object):
         self.flags = flags
 
     def set_input(self, text):
-        self.input_text = text
+        self.input_text = text.encode("UTF-8") if text else None
 
     def set_cwd(self, cwd):
         self.cwd = cwd
@@ -85,9 +85,10 @@ class Capture(GObject.Object):
             popen_args['stderr'] = subprocess.PIPE
 
         self.tried_killing = False
-        self.idle_write_id = 0
+        self.in_channel = None
         self.out_channel = None
         self.err_channel = None
+        self.in_channel_id = 0
         self.out_channel_id = 0
         self.err_channel_id = 0
 
@@ -100,62 +101,74 @@ class Capture(GObject.Object):
 
         self.emit('begin-execute')
 
+        if self.input_text is not None:
+            self.in_channel, self.in_channel_id = self.add_in_watch(self.pipe.stdin.fileno(),
+                                                                    self.on_in_writable)
+
         if self.flags & self.CAPTURE_STDOUT:
-            self.out_channel, self.out_channel_id = self.add_watch(self.pipe.stdout.fileno(),
-                                                                   self.on_output)
+            self.out_channel, self.out_channel_id = self.add_out_watch(self.pipe.stdout.fileno(),
+                                                                       self.on_output)
 
         if self.flags & self.CAPTURE_STDERR:
-            self.err_channel, self.err_channel_id = self.add_watch(self.pipe.stderr.fileno(),
-                                                                   self.on_err_output)
-
-        # IO
-        if self.input_text is not None:
-            # Write async, in chunks of something
-            self.write_buffer = self.input_text.encode('utf-8')
-
-            if self.idle_write_chunk():
-                self.idle_write_id = GLib.idle_add(self.idle_write_chunk)
+            self.err_channel, self.err_channel_id = self.add_out_watch(self.pipe.stderr.fileno(),
+                                                                       self.on_err_output)
 
         # Wait for the process to complete
         GLib.child_watch_add(GLib.PRIORITY_DEFAULT,
                              self.pipe.pid,
                              self.on_child_end)
 
-    def add_watch(self, fd, io_func):
+    def add_in_watch(self, fd, io_func):
         channel = GLib.IOChannel.unix_new(fd)
         channel.set_flags(channel.get_flags() | GLib.IOFlags.NONBLOCK)
+        channel.set_encoding(None)
         channel_id = GLib.io_add_watch(channel,
                                        GLib.PRIORITY_DEFAULT,
-                                       GLib.IOCondition.IN | GLib.IOCondition.HUP | GLib.IOCondition.ERR,
+                                       GLib.IOCondition.OUT | GLib.IOCondition.HUP | GLib.IOCondition.ERR,
                                        io_func)
         return (channel, channel_id)
 
-    def idle_write_chunk(self):
-        if not self.pipe:
-            self.idle_write_id = 0
-            return False
-
-        try:
-            l = len(self.write_buffer)
-            m = min(l, self.WRITE_BUFFER_SIZE)
+    def add_out_watch(self, fd, io_func):
+        channel = GLib.IOChannel.unix_new(fd)
+        channel.set_flags(channel.get_flags() | GLib.IOFlags.NONBLOCK)
+        channel_id = GLib.io_add_watch(channel,
+                                       GLib.PRIORITY_DEFAULT,
+                                       GLib.IOCondition.IN | GLib.IOCondition.HUP | GLib.IOCondition.ERR,
+                                       io_func)
+        return (channel, channel_id)
 
-            self.pipe.stdin.write(self.write_buffer[:m])
+    def write_chunk(self, dest, condition):
+        if condition & (GObject.IO_OUT):
+            status = GLib.IOStatus.NORMAL
+            l = len(self.input_text)
+            while status == GLib.IOStatus.NORMAL:
+                if l == 0:
+                    return False
+                m = min(l, self.WRITE_BUFFER_SIZE)
+                try:
+                    (status, length) = dest.write_chars(self.input_text, m)
+                    self.input_text = self.input_text[length:]
+                    l -= length
+                except Exception as e:
+                    return False
+            if status != GLib.IOStatus.AGAIN:
+                return False
 
-            if m == l:
-                self.write_buffer = b''
-                self.pipe.stdin.close()
+        if condition & ~(GObject.IO_OUT):
+            return False
 
-                self.idle_write_id = 0
+        return True
 
-                return False
-            else:
-                self.write_buffer = self.write_buffer[m:]
-                return True
-        except IOError:
-            self.pipe.stdin.close()
-            self.idle_write_id = 0
+    def on_in_writable(self, dest, condition):
+        ret = self.write_chunk(dest, condition)
+        if ret is False:
+            self.input_text = None
+            self.in_channel.shutdown(True)
+            self.in_channel = None
+            self.in_channel_id = 0
+            self.cleanup_pipe
 
-            return False
+        return ret
 
     def handle_source(self, source, condition, signalname):
         if condition & (GObject.IO_IN | GObject.IO_PRI):
@@ -164,8 +177,6 @@ class Capture(GObject.Object):
                 try:
                     (status, buf, length, terminator_pos) = source.read_line()
                 except Exception as e:
-                    # FIXME: why do we get here? read_line should not raise, should it?
-                    # print(e)
                     return False
                 if buf:
                     self.emit(signalname, buf)
@@ -183,9 +194,7 @@ class Capture(GObject.Object):
             self.out_channel.shutdown(True)
             self.out_channel = None
             self.out_channel_id = 0
-            # pipe should be set to None only if the err has finished too
-            if self.err_channel is None:
-                self.pipe = None
+            self.cleanup_pipe
 
         return ret
 
@@ -195,18 +204,20 @@ class Capture(GObject.Object):
             self.err_channel.shutdown(True)
             self.err_channel = None
             self.err_channel = 0
-            # pipe should be set to None only if the out has finished too
-            if self.out_channel is None:
-                self.pipe = None
+            self.cleanup_pipe
 
         return ret
 
+    def cleanup_pipe(self):
+        if self.in_channel is None and self.out_channel is None and self.err_channel is None:
+            self.pipe = None
+
     def stop(self, error_code=-1):
-        if self.idle_write_id:
-            GLib.source_remove(self.idle_write_id)
-            self.idle_write_id = 0
-            if self.pipe:
-                self.pipe.stdin.close()
+        if self.in_channel_id:
+            GLib.source_remove(self.in_channel_id)
+            self.in_channel.shutdown(True)
+            self.in_channel = None
+            self.in_channel_id = 0
 
         if self.out_channel_id:
             GLib.source_remove(self.out_channel_id)


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