summary refs log tree commit diff stats
path: root/ranger
diff options
context:
space:
mode:
authorhut <hut@lavabit.com>2010-01-24 19:04:04 +0100
committerhut <hut@lavabit.com>2010-01-24 19:04:04 +0100
commit331ebf78aea448611e0de237c867cad3ba9bfd32 (patch)
tree10d367c599cb80910054414cfcf7c26451df9df0 /ranger
parentc3cdeb9cbb3a4718d8b0d707e0864c00aca7a580 (diff)
downloadranger-331ebf78aea448611e0de237c867cad3ba9bfd32.tar.gz
added a new runner class
Diffstat (limited to 'ranger')
-rw-r--r--ranger/runner.py175
1 files changed, 175 insertions, 0 deletions
diff --git a/ranger/runner.py b/ranger/runner.py
new file mode 100644
index 00000000..c5d5a78c
--- /dev/null
+++ b/ranger/runner.py
@@ -0,0 +1,175 @@
+# Copyright (c) 2009, 2010 hut <hut@lavabit.com>
+#
+# Permission to use, copy, modify, and/or distribute this software for any
+# purpose with or without fee is hereby granted, provided that the above
+# copyright notice and this permission notice appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+"""
+This module is an abstract layer over subprocess.Popen
+
+It gives you highlevel control about how processes are run.
+
+Example:
+run = Runner(logfunc=print)
+run('sleep 2', wait=True)         # waits until the process exists
+run(['ls', '--help'], flags='p')  # pipes output to pager
+run()                             # prints an error message
+
+List of allowed flags:
+s: silent mode. output will be discarded.
+d: detach the process.
+p: redirect output to the pager
+(An uppercase key ensures that a certain flag will not be used.)
+"""
+
+import os
+import sys
+from subprocess import Popen, PIPE
+from ranger.ext.shell_escape import shell_escape
+from ranger.ext.waitpid_no_intr import waitpid_no_intr
+
+
+ALLOWED_FLAGS = 'sdpSDP'
+devnull = open(os.devnull, 'a')
+
+
+class Context(object):
+	"""
+	A context object contains data on how to run a process.
+	"""
+
+	def __init__(self, **keywords):
+		self.__dict__ = keywords
+	
+	@property
+	def filepaths(self):
+		if hasattr(self, files):
+			return [f.path for f in self.files]
+		return []
+
+	def __iter__(self):
+		"""Iterate over file paths"""
+		return iter(self.filepaths)
+
+	def squash_flags(self):
+		"""Remove duplicates and lowercase counterparts of uppercase flags"""
+		for flag in self.flags:
+			if ord(flag) <= 90:
+				bad = flag + flag.lower()
+				self.flags = ''.join(c for c in self.flags if c not in bad)
+
+
+class Runner(object):
+	def __init__(self, ui=None, logfunc=None, apps=None):
+		self.ui = ui
+		self.logfunc = logfunc
+		self.apps = apps
+	
+	def _log(self, text):
+		try:
+			self.logfunc(text)
+		except TypeError:
+			pass
+		return False
+
+	def _activate_ui(self, boolean):
+		if self.ui is not None:
+			if boolean:
+				try: self.ui.initialize()
+				except: self._log("Failed to initialize UI")
+			else:
+				try: self.ui.suspend()
+				except: self._log("Failed to suspend UI")
+	
+	def __call__(self, action=None, try_app_first=False,
+			app='default', files=None, mode=0,
+			flags='', wait=True, **popen_kws):
+		"""
+		Run the application in the way specified by the options.
+
+		Returns False if nothing can be done, None if there was an error,
+		otherwise the process object returned by Popen().
+
+		This function tries to find an action if none is defined.
+		"""
+
+		# Find an action if none was supplied by
+		# creating a Context object and passing it to
+		# an Application object.
+
+		context = Context(app=app, files=files, mode=mode,
+				flags=flags, wait=wait, popen_kws=popen_kws)
+
+		if self.apps:
+			if try_app_first and action is not None:
+				test = self.apps.apply(app, context)
+				if test:
+					action = test
+			if action is None:
+				action = self.apps.apply(app, context)
+				if action is None:
+					return self._log("No action found!")
+
+		if action is None:
+			return self._log("No way of determining the action!")
+
+		# Preconditions
+
+		context.squash_flags()
+		popen_kws = context.popen_kws  # shortcut
+
+		toggle_ui = True
+		pipe_output = False
+
+		popen_kws['args'] = action
+		if 'shell' not in popen_kws:
+			popen_kws['shell'] = isinstance(action, str)
+		if 'stdout' not in popen_kws:
+			popen_kws['stdout'] = sys.stdout
+		if 'stderr' not in popen_kws:
+			popen_kws['stderr'] = sys.stderr
+
+		# Evaluate the flags to determine keywords
+		# for Popen() and other variables
+
+		if 'p' in context.flags:
+			popen_kws['stdout'] = PIPE
+			popen_kws['stderr'] = PIPE
+			toggle_ui = False
+			pipe_output = True
+			context.wait = False
+		if 's' in context.flags or 'd' in context.flags:
+			for key in ('stdout', 'stderr', 'stdin'):
+				popen_kws[key] = devnull
+		if 'd' in context.flags:
+			toggle_ui = False
+			context.wait = False
+	
+		# Finally, run it
+
+		if toggle_ui:
+			self._activate_ui(False)
+		try:
+			process = None
+			try:
+				process = Popen(**popen_kws)
+			except:
+				self._log("Failed to run: " + str(action))
+			else:
+				if context.wait:
+					waitpid_no_intr(process.pid)
+		finally:
+			if toggle_ui:
+				self._activate_ui(True)
+			if pipe_output and process:
+				return self(action='less', app='pager', try_app_first=True,
+						stdin=process.stdout)
+			return process
05 +0200 committer hut <hut@lavabit.com> 2011-09-29 02:52:15 +0200 A little README update' href='/akspecs/ranger/commit/README?h=v1.9.2&id=4a383291629a6a1e0bbd531a02466d267d553c5e'>4a383291 ^
78a7d762 ^
e952d6cb ^
78a7d762 ^
e952d6cb ^
4ea0f69a ^

7838675f ^

07069888 ^

07069888 ^
7838675f ^

1891697f ^
755e7df1 ^
a3d5f44b ^
66a8d4ff ^
7838675f ^
7dc8fef8 ^
80a93f0b ^
500cf259 ^

7dc8fef8 ^


80a93f0b ^
240394a4 ^



240394a4 ^
7dc8fef8 ^
240394a4 ^

96004e71 ^
240394a4 ^

96004e71 ^
240394a4 ^



e5fb3d74 ^
240394a4 ^

240394a4 ^
7dc8fef8 ^



7b33b517 ^

176e8a68 ^
7b33b517 ^
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100