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 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171
|
"""
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
#-----------------------------------------------------------
# posix external command execution
#-----------------------------------------------------------
def posix_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.
"""
__tracebackhide__ = True
import popen2
import errno
#print "execing", cmd
child = popen2.Popen3(cmd, 1)
stdin, stdout, stderr = child.tochild, child.fromchild, child.childerr
stdin.close()
# XXX sometimes we get a blocked r.read() call (see below)
# although select told us there is something to read.
# only the next three lines appear to prevent
# the read call from blocking infinitely.
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())
#fcntl.fcntl(stdout, fcntl.F_SETFL, os.O_NONBLOCK)
#fcntl.fcntl(stderr, fcntl.F_SETFL, os.O_NONBLOCK)
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() # XXX see XXX above
except IOError, io:
if io.args[0] == errno.EAGAIN:
continue
# Connection Lost
raise
except OSError, ose:
if ose.errno == errno.EPIPE:
# Connection Lost
raise
if ose.errno == errno.EAGAIN: # MacOS-X does this
continue
raise
if not data:
r.close()
continue
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)
#-----------------------------------------------------------
# simple win32 external command execution
#-----------------------------------------------------------
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.
"""
#print "*****", cmd
# the following quoting is only valid for CMD.EXE, not COMMAND.COM
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)
#
# choose correct platform-version
#
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
# export the exception under the name 'py.process.cmdexec.Error'
cmdexec.Error = ExecutionFailed
try:
ExecutionFailed.__module__ = 'py.process.cmdexec'
ExecutionFailed.__name__ = 'Error'
except (AttributeError, TypeError):
pass
|