diff --git a/_py/process/cmdexec.py b/_py/process/cmdexec.py index c0f7d5753..aa2031f10 100644 --- a/_py/process/cmdexec.py +++ b/_py/process/cmdexec.py @@ -1,151 +1,28 @@ """ -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 subprocess import py from subprocess import Popen, PIPE -#----------------------------------------------------------- -# posix external command execution -#----------------------------------------------------------- -def posix_exec_cmd(cmd): - """ return output of executing 'cmd'. +def cmdexec(cmd): + """ return output of executing 'cmd' in a separate process. - raise ExecutionFailed exeception if the command failed. + raise cmdexec.ExecutionFailed exeception if the command failed. the exception will provide an 'err' attribute containing the error-output from the command. """ - #__tracebackhide__ = True - - import errno - - child = Popen(cmd, shell=True, stdout=PIPE, stderr=PIPE, close_fds=True) - stdin, stdout, stderr = child.stdin, child.stdout, child.stderr - - # 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 = [x for x in [stdout, stderr] if x and not x.closed] - if not r_list: - break - try: - r_list = select.select(r_list, [], [])[0] - except (select.error, IOError): - se = sys.exc_info()[1] - 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 = sys.exc_info()[1] - if io.args[0] == errno.EAGAIN: - continue - # Connection Lost - raise - except OSError: - ose = sys.exc_info()[1] - 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, - joiner(out), joiner(err)) - return joiner(out) - -def joiner(out): - encoding = sys.getdefaultencoding() - return "".join([py.builtin._totext(x, encoding) for x in 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() + process = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + out, err = process.communicate() + out = py.builtin._totext(out, sys.getdefaultencoding()) + err = py.builtin._totext(err, sys.getdefaultencoding()) + status = process.poll() 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) @@ -157,16 +34,6 @@ class ExecutionFailed(py.error.Error): 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 diff --git a/testing/process/test_cmdexec.py b/testing/process/test_cmdexec.py index ad291d64f..c8965ab00 100644 --- a/testing/process/test_cmdexec.py +++ b/testing/process/test_cmdexec.py @@ -18,6 +18,8 @@ class Test_exec_cmd: except cmdexec.Error: e = exvalue() assert e.status == 1 + assert py.builtin._istext(e.out) + assert py.builtin._istext(e.err) def test_err(self): try: @@ -28,16 +30,3 @@ class Test_exec_cmd: assert hasattr(e, 'err') assert hasattr(e, 'out') assert e.err or e.out - -def test_cmdexec_selection(): - from _py.process import cmdexec - if py.std.sys.platform == "win32": - assert py.process.cmdexec == cmdexec.win32_exec_cmd - elif hasattr(py.std.sys, 'pypy') or hasattr(py.std.sys, 'pypy_objspaceclass'): - assert py.process.cmdexec == cmdexec.popen3_exec_cmd - else: - assert py.process.cmdexec == cmdexec.posix_exec_cmd - - - -