172 lines
		
	
	
		
			5.2 KiB
		
	
	
	
		
			Python
		
	
	
	
			
		
		
	
	
			172 lines
		
	
	
		
			5.2 KiB
		
	
	
	
		
			Python
		
	
	
	
| """
 | |
| 
 | |
| 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
 |