Below is the file 'goatpy/utility.py' from this revision. You can also download the file.


import popen2
import select
import fcntl
import os

def set_nonblocking(fd):
	fl = fcntl.fcntl(fd, fcntl.F_GETFL)
	fcntl.fcntl(fd, fcntl.F_SETFL, fl | os.O_NDELAY)

def run_command(command, timeout=None):
	"returns a tuple of (was_timeout, exit_code, data_read)"
	p = popen2.Popen3(command, capturestderr=True)
	set_nonblocking(p.fromchild)
	set_nonblocking(p.childerr)
	fromchild_read = ""
	childerr_read = ""
	was_timeout = False
	while 1:
		ro, rw, re = select.select([p.fromchild], [], [p.childerr], timeout)
		if not ro and not rw and not re:
			was_timeout = True
			break
		if p.fromchild in ro:
			recv = p.fromchild.read()
			if recv == "": break
			fromchild_read += recv
		if p.childerr in re:
			recv = p.childerr.read()
			if recv == "": break
			childerr_read += recv
	if not was_timeout:
		# check for any data we might have missed (due to a premature break)
		# (if there isn't anything we just get a IOError, which we don't mind
		try: fromchild_read += p.fromchild.read()
		except IOError: pass
		try: childerr_read += p.childerr.read()
		except IOError: pass
	p.fromchild.close()
	p.tochild.close()
	# if there wasn't a timeout, the program should have exited; in which case we should wait() for it
	# otherwise, it might be hung, so the parent should wait for it.
	# (wrap in a try: except: just in case some other thread happens to wait() and grab ours; god wrapping
	# python around UNIX is horrible sometimes)
	exitcode = None
	try:
		if not was_timeout: exitcode = p.wait() >> 8
	except: pass
	return { 'run_command' : command,
	         'timeout' : was_timeout,
		 'exitcode' : exitcode,
		 'fromchild' : fromchild_read,
		 'childerr' : childerr_read }

def iter_command(command, timeout=None):
	p = popen2.Popen3(command, capturestderr=True)
	set_nonblocking(p.fromchild)
	set_nonblocking(p.childerr)
	fromchild_read = ""
	childerr_read = ""
	was_timeout = False
	while 1:
		ro, rw, re = select.select([p.fromchild], [], [p.childerr], timeout)
		if not ro and not rw and not re:
			was_timeout = True
			break
		if p.fromchild in ro:
			recv = p.fromchild.read()
			if recv == "": break
			fromchild_read += recv
			while 1:
				nl = fromchild_read.find('\n')
				if nl == -1: break
				yield fromchild_read[:nl]
				fromchild_read = fromchild_read[nl+1:]
		if p.childerr in re:
			recv = p.childerr.read()
			if recv == "": break
			childerr_read += recv
	if not was_timeout:
		# check for any data we might have missed (due to a premature break)
		# (if there isn't anything we just get a IOError, which we don't mind
		try: fromchild_read += p.fromchild.read()
		except IOError: pass
		try: childerr_read += p.childerr.read()
		except IOError: pass
	p.fromchild.close()
	p.tochild.close()
	# yield anything left over
	to_yield = fromchild_read.split('\n')
	while len(to_yield): yield to_yield.pop()
	# call wait()
	try:
		if not was_timeout: p.wait()
	except: pass
	if len(childerr_read): raise Exception("data on stderr", childerr_read)
	if was_timeout: raise Exception("command timeout")