[kupfer] commands: Implement Run (Get Output) using AsyncCommand



commit c61f81f808b5241357504f2960c789d8234a8c49
Author: Ulrik Sverdrup <ulrik sverdrup gmail com>
Date:   Wed May 5 01:01:25 2010 +0200

    commands: Implement Run (Get Output) using AsyncCommand
    
    Finally we have implemented "Get results from commandline" in a sane
    way: completely asynchronous, with a timeout to kill the process, and
    with asynchronous return of output to the user. (Kupfer is starting to
    get good :-)
    
    (Also change the from os import (..) in commands, it doesn't make any
    sense now.)

 kupfer/plugin/commands.py |   47 ++++++++++++++++++++++++++++++++++++++++----
 kupfer/utils.py           |   11 ++++++++-
 2 files changed, 51 insertions(+), 7 deletions(-)
---
diff --git a/kupfer/plugin/commands.py b/kupfer/plugin/commands.py
index 80312d4..c1edbb8 100644
--- a/kupfer/plugin/commands.py
+++ b/kupfer/plugin/commands.py
@@ -6,15 +6,51 @@ __version__ = ""
 __author__ = "Ulrik Sverdrup <ulrik sverdrup gmail com>"
 
 import os
-# import symbols in tight loop to local namespace
-from os import access, R_OK, X_OK, path
+import shlex
 
 import gobject
 
-from kupfer.objects import TextSource, Leaf, TextLeaf
+from kupfer.objects import TextSource, Leaf, TextLeaf, Action
 from kupfer.obj.fileactions import Execute
 from kupfer import utils, icons
+from kupfer import commandexec
+from kupfer import kupferstring
+from kupfer import pretty
 
+def unicode_shlex_split(ustr, **kwargs):
+	"""shlex.split is depressingly broken on unicode input"""
+	s_str = ustr.encode("UTF-8")
+	return [kupferstring.tounicode(t) for t in shlex.split(s_str, **kwargs)]
+
+
+class GetOutput (Action):
+	def __init__(self):
+		Action.__init__(self, _("Run (Get Output)"))
+
+	def activate(self, leaf):
+		# use shlex to allow simple quoting
+		commandline = leaf.object
+		try:
+			argv = unicode_shlex_split(commandline)
+		except ValueError:
+			# Exception raised on unpaired quotation marks
+			if " " in commandline:
+				argv = commandline.split(None, 1)
+			else:
+				argv = [commandline]
+		ctx = commandexec.DefaultActionExecutionContext()
+		token = ctx.get_async_token()
+		pretty.print_debug(__name__, "Spawning with timeout 15 seconds")
+		acom = utils.AsyncCommand(argv, self.finish_callback, 15)
+		acom.token = token
+
+	def finish_callback(self, acommand, output):
+		ctx = commandexec.DefaultActionExecutionContext()
+		leaf = TextLeaf(kupferstring.fromlocale(output))
+		ctx.register_late_result(acommand.token, leaf)
+
+	def get_description(self):
+		return _("Run program and return its output")
 
 class Command (TextLeaf):
 	def __init__(self, exepath, name):
@@ -24,6 +60,7 @@ class Command (TextLeaf):
 	def get_actions(self):
 		yield Execute(quoted=False)
 		yield Execute(in_terminal=True, quoted=False)
+		yield GetOutput()
 
 	def get_description(self):
 		args = u" ".join(unicode(self).split(None, 1)[1:])
@@ -54,10 +91,10 @@ class CommandTextSource (TextSource):
 		# iterate over $PATH directories
 		PATH = os.environ.get("PATH") or os.defpath
 		for execdir in PATH.split(os.pathsep):
-			exepath = path.join(execdir, firstword)
+			exepath = os.path.join(execdir, firstword)
 			# use filesystem encoding here
 			exepath = gobject.filename_from_utf8(exepath)
-			if access(exepath, R_OK | X_OK) and path.isfile(exepath):
+			if os.access(exepath, os.R_OK|os.X_OK) and os.path.isfile(exepath):
 				yield Command(exepath, text)
 				break
 	def get_description(self):
diff --git a/kupfer/utils.py b/kupfer/utils.py
index 6ca51d9..69bd5b4 100644
--- a/kupfer/utils.py
+++ b/kupfer/utils.py
@@ -70,6 +70,10 @@ def locale_sort(seq, key=unicode):
 	seq.sort(cmp=locale_cmp)
 	return seq
 
+def _argv_to_locale(argv):
+	"encode unicode strings in @argv according to the locale encoding"
+	return [kupferstring.tolocale(A) if isinstance(A, unicode) else A
+			for A in argv]
 
 class AsyncCommand (object):
 	# the maximum input (bytes) we'll read in one shot (one io_callback)
@@ -90,6 +94,10 @@ class AsyncCommand (object):
 		self.killed = False
 		self.finished = False
 		self.finish_callback = finish_callback
+
+		argv = _argv_to_locale(argv)
+		pretty.print_debug(__name__, "AsyncCommand:", argv)
+
 		flags = (glib.SPAWN_SEARCH_PATH | glib.SPAWN_STDERR_TO_DEV_NULL |
 		         glib.SPAWN_DO_NOT_REAP_CHILD)
 		pid, stdin_fd, stdout_fd, stderr_fd = \
@@ -132,8 +140,7 @@ class AsyncCommand (object):
 
 def spawn_async(argv, in_dir="."):
 	pretty.print_debug(__name__, "Spawn commandline", argv, in_dir)
-	argv = [kupferstring.tolocale(A) if isinstance(A, unicode) else A
-			for A in argv]
+	argv = _argv_to_locale(argv)
 	try:
 		return gobject.spawn_async (argv, working_directory=in_dir,
 				flags=gobject.SPAWN_SEARCH_PATH)



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