[svn r37264] create the new development trunk
--HG-- branch : trunk
This commit is contained in:
1
py/process/__init__.py
Normal file
1
py/process/__init__.py
Normal file
@@ -0,0 +1 @@
|
||||
#
|
||||
163
py/process/cmdexec.py
Normal file
163
py/process/cmdexec.py
Normal file
@@ -0,0 +1,163 @@
|
||||
"""
|
||||
|
||||
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
|
||||
|
||||
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
|
||||
|
||||
|
||||
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
|
||||
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
|
||||
1
py/process/testing/__init__.py
Normal file
1
py/process/testing/__init__.py
Normal file
@@ -0,0 +1 @@
|
||||
#
|
||||
25
py/process/testing/test_cmdexec.py
Normal file
25
py/process/testing/test_cmdexec.py
Normal file
@@ -0,0 +1,25 @@
|
||||
from py import test
|
||||
from py.process import cmdexec
|
||||
|
||||
class Test_exec_cmd:
|
||||
def test_simple(self):
|
||||
out = cmdexec('echo hallo')
|
||||
assert out.strip() == 'hallo'
|
||||
|
||||
def test_simple_error(self):
|
||||
test.raises (cmdexec.Error, cmdexec, 'exit 1')
|
||||
|
||||
def test_simple_error_exact_status(self):
|
||||
try:
|
||||
cmdexec('exit 1')
|
||||
except cmdexec.Error, e:
|
||||
assert e.status == 1
|
||||
|
||||
def test_err(self):
|
||||
try:
|
||||
cmdexec('echoqweqwe123 hallo')
|
||||
raise AssertionError, "command succeeded but shouldn't"
|
||||
except cmdexec.Error, e:
|
||||
assert hasattr(e, 'err')
|
||||
assert hasattr(e, 'out')
|
||||
assert e.err or e.out
|
||||
Reference in New Issue
Block a user