[kupfer] commands: Add actions Filter through Command and Send to Command
- From: Ulrik Sverdrup <usverdrup src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [kupfer] commands: Add actions Filter through Command and Send to Command
- Date: Mon, 28 Feb 2011 19:01:52 +0000 (UTC)
commit 62b8c8cbe9842c1d8114e379988ce2ee37194675
Author: Ulrik Sverdrup <ulrik sverdrup gmail com>
Date: Mon Feb 28 19:36:46 2011 +0100
commands: Add actions Filter through Command and Send to Command
Allow to exchange text with commands (script/executables in the
catalog itself) by stdin/stdout. Add a scripts folder to your catalog
to use (or add scripts to Favorites)
Not final whether both the commands will stay.
Add stdin supplication feature to utils.AsyncCommand for this purpose.
kupfer/plugin/commands.py | 84 ++++++++++++++++++++++++++++++++++++++++++++-
kupfer/utils.py | 41 ++++++++++++++++++++--
2 files changed, 121 insertions(+), 4 deletions(-)
---
diff --git a/kupfer/plugin/commands.py b/kupfer/plugin/commands.py
index 7194416..daf33ab 100644
--- a/kupfer/plugin/commands.py
+++ b/kupfer/plugin/commands.py
@@ -1,5 +1,9 @@
__kupfer_name__ = _("Shell Commands")
__kupfer_sources__ = ()
+__kupfer_actions__ = (
+ "WriteToCommand",
+ "FilterThroughCommand",
+ )
__kupfer_text_sources__ = ("CommandTextSource",)
__description__ = _("Run commandline programs")
__version__ = ""
@@ -10,7 +14,7 @@ import shlex
import gobject
-from kupfer.objects import TextSource, Leaf, TextLeaf, Action
+from kupfer.objects import TextSource, Leaf, TextLeaf, Action, FileLeaf
from kupfer.obj.fileactions import Execute
from kupfer import utils, icons
from kupfer import commandexec
@@ -49,6 +53,84 @@ class GetOutput (Action):
def get_description(self):
return _("Run program and return its output")
+class WriteToCommand (Action):
+ def __init__(self):
+ Action.__init__(self, _("Send to Command..."))
+
+ def activate(self, leaf, iobj):
+ # use shlex to allow simple quoting
+ commandline = iobj.object
+ try:
+ argv = unicode_shlex_split(commandline)
+ except ValueError:
+ # Exception raised on unpaired quotation marks
+ argv = commandline.split(None, 1)
+ ctx = commandexec.DefaultActionExecutionContext()
+ token = ctx.get_async_token()
+ pretty.print_debug(__name__, "Spawning without timeout")
+ acom = utils.AsyncCommand(argv, self.finish_callback, None,
+ stdin=leaf.object)
+ acom.token = token
+
+ def item_types(self):
+ yield TextLeaf
+
+ def requires_object(self):
+ return True
+
+ def object_types(self):
+ yield FileLeaf
+
+ def valid_object(self, iobj, for_item=None):
+ return not iobj.is_dir() and os.access(iobj.object, os.X_OK | os.R_OK)
+
+ def finish_callback(self, acommand, stdout, stderr):
+ pretty.print_debug(__name__, "Exited:", acommand)
+ pass
+
+ def get_description(self):
+ return _("Run program and supply text on the standard input")
+
+class FilterThroughCommand (Action):
+ def __init__(self):
+ Action.__init__(self, _("Filter through Command..."))
+
+ def activate(self, leaf, iobj):
+ # use shlex to allow simple quoting
+ commandline = iobj.object
+ try:
+ argv = unicode_shlex_split(commandline)
+ except ValueError:
+ # Exception raised on unpaired quotation marks
+ argv = commandline.split(None, 1)
+ ctx = commandexec.DefaultActionExecutionContext()
+ token = ctx.get_async_token()
+ pretty.print_debug(__name__, "Spawning without timeout")
+ acom = utils.AsyncCommand(argv, self.finish_callback, None,
+ stdin=leaf.object)
+ acom.token = token
+
+ def item_types(self):
+ yield TextLeaf
+
+ def requires_object(self):
+ return True
+
+ def object_types(self):
+ yield FileLeaf
+
+ def valid_object(self, iobj, for_item=None):
+ return not iobj.is_dir() and os.access(iobj.object, os.X_OK | os.R_OK)
+
+ def finish_callback(self, acommand, stdout, stderr):
+ pretty.print_debug(__name__, "Exited:", acommand)
+ ctx = commandexec.DefaultActionExecutionContext()
+ leaf = TextLeaf(kupferstring.fromlocale(stdout))
+ ctx.register_late_result(acommand.token, leaf)
+
+ def get_description(self):
+ return _("Run program and supply text on the standard input")
+
class Command (TextLeaf):
def __init__(self, exepath, name):
TextLeaf.__init__(self, name, name)
diff --git a/kupfer/utils.py b/kupfer/utils.py
index b1c3e1b..b813588 100644
--- a/kupfer/utils.py
+++ b/kupfer/utils.py
@@ -84,14 +84,19 @@ class AsyncCommand (object):
when command is killed after @timeout_s seconds, whichever
comes first.
+ If @timeout_s is None, no timeout is used
+
+ If stdin is a byte string, it is supplied on the command's stdin.
+
finish_callback -> (AsyncCommand, stdout_output, stderr_output)
"""
# the maximum input (bytes) we'll read in one shot (one io_callback)
max_input_buf = 512 * 1024
- def __init__(self, argv, finish_callback, timeout_s):
+ def __init__(self, argv, finish_callback, timeout_s, stdin=None):
self.stdout = []
self.stderr = []
+ self.stdin = []
self.timeout = False
self.killed = False
self.finished = False
@@ -104,13 +109,29 @@ class AsyncCommand (object):
pid, stdin_fd, stdout_fd, stderr_fd = \
glib.spawn_async(argv, standard_output=True, standard_input=True,
standard_error=True, flags=flags)
- os.close(stdin_fd)
+
+ if stdin:
+ self.stdin[:] = self._split_string(stdin, self.max_input_buf)
+ in_io_flags = glib.IO_OUT | glib.IO_ERR | glib.IO_HUP | glib.IO_NVAL
+ glib.io_add_watch(stdin_fd, in_io_flags, self._in_io_callback,
+ self.stdin)
+ else:
+ os.close(stdin_fd)
+
io_flags = glib.IO_IN | glib.IO_ERR | glib.IO_HUP | glib.IO_NVAL
glib.io_add_watch(stdout_fd, io_flags, self._io_callback, self.stdout)
glib.io_add_watch(stderr_fd, io_flags, self._io_callback, self.stderr)
self.pid = pid
glib.child_watch_add(pid, self._child_callback)
- glib.timeout_add_seconds(timeout_s, self._timeout_callback)
+ if timeout_s is not None:
+ glib.timeout_add_seconds(timeout_s, self._timeout_callback)
+
+ def _split_string(self, s, length):
+ """Split @s in pieces of @length"""
+ L = []
+ for i in xrange(0, len(s)//length + 1):
+ L.append(s[i*length:(i+1)*length])
+ return L
def _io_callback(self, sourcefd, condition, databuf):
if condition & glib.IO_IN:
@@ -118,6 +139,20 @@ class AsyncCommand (object):
return True
return False
+ def _in_io_callback(self, sourcefd, condition, databuf):
+ """write to child's stdin"""
+ if condition & glib.IO_OUT:
+ if not databuf:
+ os.close(sourcefd)
+ return False
+ s = databuf.pop(0)
+ written = os.write(sourcefd, s)
+ if written < len(s):
+ databuf.insert(0, s[written:])
+ pretty.print_debug(__name__, "Wrote", repr(s[:written]))
+ return True
+ return False
+
def _child_callback(self, pid, condition):
self.finished = True
self.finish_callback(self, "".join(self.stdout), "".join(self.stderr))
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]