[gedit/wip/etfixes: 2/2] external tools: Use a GIOchannel also for stdin
- From: Paolo Borelli <pborelli src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gedit/wip/etfixes: 2/2] external tools: Use a GIOchannel also for stdin
- Date: Sat, 29 Mar 2014 14:40:39 +0000 (UTC)
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]