""" |
|
module defining basic hook for executing commands |
in a - as much as possible - platform independent way. |
|
Current list: |
|
exec_cmd(cmd) executes the given command and returns output |
or ExecutionFailed exception (if exit status!=0) |
|
""" |
|
import os, sys |
import py |
|
|
|
|
def posix_exec_cmd(cmd, debug=False): |
""" return output of executing 'cmd'. |
|
raise ExecutionFailed exeception if the command failed. |
the exception will provide an 'err' attribute containing |
the error-output from the command. |
""" |
__tracebackhide__ = True |
import popen2 |
import errno |
|
|
child = popen2.Popen3(cmd, 1) |
stdin, stdout, stderr = child.tochild, child.fromchild, child.childerr |
stdin.close() |
|
|
|
|
|
import fcntl |
def set_non_block(fd): |
flags = fcntl.fcntl(fd, fcntl.F_GETFL) |
flags = flags | os.O_NONBLOCK |
fcntl.fcntl(fd, fcntl.F_SETFL, flags) |
set_non_block(stdout.fileno()) |
set_non_block(stderr.fileno()) |
|
|
|
import select |
out, err = [], [] |
while 1: |
r_list = filter(lambda x: x and not x.closed, [stdout, stderr]) |
if not r_list: |
break |
try: |
r_list = select.select(r_list, [], [])[0] |
except (select.error, IOError), se: |
if se.args[0] == errno.EINTR: |
continue |
else: |
raise |
for r in r_list: |
try: |
data = r.read() |
except IOError, io: |
if io.args[0] == errno.EAGAIN: |
continue |
|
raise |
except OSError, ose: |
if ose.errno == errno.EPIPE: |
|
raise |
if ose.errno == errno.EAGAIN: |
continue |
raise |
|
if not data: |
r.close() |
continue |
if debug: |
sys.stderr.write(data) |
if r is stdout: |
out.append(data) |
else: |
err.append(data) |
pid, systemstatus = os.waitpid(child.pid, 0) |
if pid != child.pid: |
raise ExecutionFailed, "child process disappeared during: "+ cmd |
if systemstatus: |
if os.WIFSIGNALED(systemstatus): |
status = os.WTERMSIG(systemstatus) + 128 |
else: |
status = os.WEXITSTATUS(systemstatus) |
raise ExecutionFailed(status, systemstatus, cmd, |
''.join(out), ''.join(err)) |
return "".join(out) |
|
|
|
|
def win32_exec_cmd(cmd): |
""" return output of executing 'cmd'. |
|
raise ExecutionFailed exeception if the command failed. |
the exception will provide an 'err' attribute containing |
the error-output from the command. |
|
Note that this method can currently deadlock because |
we don't have WaitForMultipleObjects in the std-python api. |
|
Further note that the rules for quoting are very special |
under Windows. Do a HELP CMD in a shell, and tell me if |
you understand this. For now, I try to do a fix. |
""" |
|
|
|
cmd_quoting = True |
try: |
if os.environ['COMSPEC'].upper().endswith('COMMAND.COM'): |
cmd_quoting = False |
except KeyError: |
pass |
if cmd_quoting: |
if '"' in cmd and not cmd.startswith('""'): |
cmd = '"%s"' % cmd |
|
return popen3_exec_cmd(cmd) |
|
def popen3_exec_cmd(cmd): |
stdin, stdout, stderr = os.popen3(cmd) |
out = stdout.read() |
err = stderr.read() |
stdout.close() |
stderr.close() |
status = stdin.close() |
if status: |
raise ExecutionFailed(status, status, cmd, out, err) |
return out |
|
def pypy_exec_cmd(cmd): |
return popen3_exec_cmd(cmd) |
|
class ExecutionFailed(py.error.Error): |
def __init__(self, status, systemstatus, cmd, out, err): |
Exception.__init__(self) |
self.status = status |
self.systemstatus = systemstatus |
self.cmd = cmd |
self.err = err |
self.out = out |
|
def __str__(self): |
return "ExecutionFailed: %d %s\n%s" %(self.status, self.cmd, self.err) |
|
|
|
|
if sys.platform == 'win32': |
cmdexec = win32_exec_cmd |
elif hasattr(sys, 'pypy') or hasattr(sys, 'pypy_objspaceclass'): |
cmdexec = popen3_exec_cmd |
else: |
cmdexec = posix_exec_cmd |
|
|
cmdexec.Error = ExecutionFailed |
try: |
ExecutionFailed.__module__ = 'py.process.cmdexec' |
ExecutionFailed.__name__ = 'Error' |
except (AttributeError, TypeError): |
pass |
|