"""
def __init__(self, dry = False):
self.dry = dry
+ self.cwd = None
def call(self, *args, **kwargs):
"""
Performs a system call. The actual executable and options should
# ourself, and then setting up a
# SIGCHILD handler to write a single byte to the pipe to get
# us out of select() when a subprocess exits.
- proc = subprocess.Popen(args, stdout=stdout, stderr=stderr, stdin=stdin)
+ proc = subprocess.Popen(args, stdout=stdout, stderr=stderr, stdin=stdin, cwd=self.cwd, )
if self._async(proc, args, **kwargs):
return proc
stdout, stderr = proc.communicate(kwargs["input"])
+ # can occur if we were doing interactive communication; i.e.
+ # we didn't pass in PIPE.
+ if stdout is None:
+ stdout = ""
+ if stderr is None:
+ stderr = ""
if not kwargs["interactive"]:
if kwargs["strip"]:
self._log(None, stderr)
logging.debug("STDERR:\n" + stderr)
def _wait(self):
pass
- def _async(self):
+ def _async(self, *args, **kwargs):
return False
def callAsUser(self, *args, **kwargs):
"""
on working directory context. Keyword arguments are the
same as :meth:`call`.
"""
+ if os.getuid():
+ return self.call(*args, **kwargs)
uid = os.stat(os.getcwd()).st_uid
# consider also checking ruid?
if uid != os.geteuid():
"""
kwargs["strip"] = True
return self.call(*args, **kwargs)
+ def setcwd(self, cwd):
+ """
+ Sets the directory processes are executed in. This sets a value
+ to be passed as the ``cwd`` argument to ``subprocess.Popen``.
+ """
+ self.cwd = cwd
class ParallelShell(Shell):
"""
Blocking call that waits for an open subprocess slot. This is
automatically called by :meth:`Shell.call`.
"""
- # XXX: This API sucks; the actuall call/callAsUser call should
+ # XXX: This API sucks; the actual call/callAsUser call should
# probably block automatically (unless I have a good reason not to)
# bail out immediately on initial ramp up
if len(self.running) < self.max: return
return
on_success(stdout, stderr)
+# Setup a convenience global instance
+shell = Shell()
+call = shell.call
+callAsUser = shell.callAsUser
+safeCall = shell.safeCall
+eval = shell.eval
class DummyParallelShell(ParallelShell):
"""Same API as :class:`ParallelShell`, but doesn't actually