diff --git a/ChangeLog b/ChangeLog index c12f24f..5903081 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,11 @@ +2009-04-19 Thomas Vander Stichele + + * morituri/extern (added): + * morituri/extern/__init__.py (added): + * morituri/extern/asyncsub.py (added): + Add extern module; add asynchronous subprocess Popen from + http://code.activestate.com/recipes/440554/ + 2009-04-15 Thomas Vander Stichele * examples/gtkchecksum.py: diff --git a/morituri/extern/__init__.py b/morituri/extern/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/morituri/extern/asyncsub.py b/morituri/extern/asyncsub.py new file mode 100644 index 0000000..219faf3 --- /dev/null +++ b/morituri/extern/asyncsub.py @@ -0,0 +1,168 @@ +# -*- Mode: Python -*- +# vi:si:et:sw=4:sts=4:ts=4 + +# from http://code.activestate.com/recipes/440554/ + +import os +import subprocess +import errno +import time +import sys + +PIPE = subprocess.PIPE + +if subprocess.mswindows: + from win32file import ReadFile, WriteFile + from win32pipe import PeekNamedPipe + import msvcrt +else: + import select + import fcntl + +class Popen(subprocess.Popen): + def recv(self, maxsize=None): + return self._recv('stdout', maxsize) + + def recv_err(self, maxsize=None): + return self._recv('stderr', maxsize) + + def send_recv(self, input='', maxsize=None): + return self.send(input), self.recv(maxsize), self.recv_err(maxsize) + + def get_conn_maxsize(self, which, maxsize): + if maxsize is None: + maxsize = 1024 + elif maxsize < 1: + maxsize = 1 + return getattr(self, which), maxsize + + def _close(self, which): + getattr(self, which).close() + setattr(self, which, None) + + if subprocess.mswindows: + def send(self, input): + if not self.stdin: + return None + + try: + x = msvcrt.get_osfhandle(self.stdin.fileno()) + (errCode, written) = WriteFile(x, input) + except ValueError: + return self._close('stdin') + except (subprocess.pywintypes.error, Exception), why: + if why[0] in (109, errno.ESHUTDOWN): + return self._close('stdin') + raise + + return written + + def _recv(self, which, maxsize): + conn, maxsize = self.get_conn_maxsize(which, maxsize) + if conn is None: + return None + + try: + x = msvcrt.get_osfhandle(conn.fileno()) + (read, nAvail, nMessage) = PeekNamedPipe(x, 0) + if maxsize < nAvail: + nAvail = maxsize + if nAvail > 0: + (errCode, read) = ReadFile(x, nAvail, None) + except ValueError: + return self._close(which) + except (subprocess.pywintypes.error, Exception), why: + if why[0] in (109, errno.ESHUTDOWN): + return self._close(which) + raise + + if self.universal_newlines: + read = self._translate_newlines(read) + return read + + else: + def send(self, input): + if not self.stdin: + return None + + if not select.select([], [self.stdin], [], 0)[1]: + return 0 + + try: + written = os.write(self.stdin.fileno(), input) + except OSError, why: + if why[0] == errno.EPIPE: #broken pipe + return self._close('stdin') + raise + + return written + + def _recv(self, which, maxsize): + conn, maxsize = self.get_conn_maxsize(which, maxsize) + if conn is None: + return None + + flags = fcntl.fcntl(conn, fcntl.F_GETFL) + if not conn.closed: + fcntl.fcntl(conn, fcntl.F_SETFL, flags| os.O_NONBLOCK) + + try: + if not select.select([conn], [], [], 0)[0]: + return '' + + r = conn.read(maxsize) + if not r: + return self._close(which) + + if self.universal_newlines: + r = self._translate_newlines(r) + return r + finally: + if not conn.closed: + fcntl.fcntl(conn, fcntl.F_SETFL, flags) + +message = "Other end disconnected!" + +def recv_some(p, t=.1, e=1, tr=5, stderr=0): + if tr < 1: + tr = 1 + x = time.time()+t + y = [] + r = '' + pr = p.recv + if stderr: + pr = p.recv_err + while time.time() < x or r: + r = pr() + if r is None: + if e: + raise Exception(message) + else: + break + elif r: + y.append(r) + else: + time.sleep(max((x-time.time())/tr, 0)) + return ''.join(y) + +def send_all(p, data): + while len(data): + sent = p.send(data) + if sent is None: + raise Exception(message) + data = buffer(data, sent) + +if __name__ == '__main__': + if sys.platform == 'win32': + shell, commands, tail = ('cmd', ('dir /w', 'echo HELLO WORLD'), '\r\n') + else: + shell, commands, tail = ('sh', ('ls', 'echo HELLO WORLD'), '\n') + + a = Popen(shell, stdin=PIPE, stdout=PIPE) + print recv_some(a), + for cmd in commands: + send_all(a, cmd + tail) + print recv_some(a), + send_all(a, 'exit' + tail) + print recv_some(a, e=0) + a.wait()