--- subprocess.py.r65475 2008-10-17 16:50:51.000000000 +0200 +++ subprocess.py 2008-10-17 16:44:41.000000000 +0200 @@ -421,7 +421,7 @@ def _cleanup(): for inst in _active[:]: - if inst._internal_poll(_deadstate=sys.maxint) >= 0: + if inst.poll(_deadstate=sys.maxint) >= 0: try: _active.remove(inst) except ValueError: @@ -433,6 +433,35 @@ STDOUT = -2 +def _no_intr(iofunc): + """Make iofunc retry on EINTR""" + def intr_protected(*args, **kwargs): + while True: + try: + return iofunc(*args, **kwargs) + except (OSError, IOError), e: + if e.errno == errno.EINTR: + continue + raise + except select.error, e: + if e.args[0] == errno.EINTR: + continue + raise + return intr_protected + + +class _NoINTR: + """Protect callable from EINTR""" + def __init__(self, cls): + self.cls = cls + def __getattr__(self, attrname): + attr = getattr(self.cls, attrname) + if callable(attr): + return _no_intr(attr) + else: + return attr + + def call(*popenargs, **kwargs): """Run command with arguments. Wait for command to complete, then return the returncode attribute. @@ -610,17 +639,17 @@ errread = None if p2cwrite: - self.stdin = os.fdopen(p2cwrite, 'wb', bufsize) + self.stdin = _NoINTR(os.fdopen(p2cwrite, 'wb', bufsize)) if c2pread: if universal_newlines: - self.stdout = os.fdopen(c2pread, 'rU', bufsize) + self.stdout = _NoINTR(os.fdopen(c2pread, 'rU', bufsize)) else: - self.stdout = os.fdopen(c2pread, 'rb', bufsize) + self.stdout = _NoINTR(os.fdopen(c2pread, 'rb', bufsize)) if errread: if universal_newlines: - self.stderr = os.fdopen(errread, 'rU', bufsize) + self.stderr = _NoINTR(os.fdopen(errread, 'rU', bufsize)) else: - self.stderr = os.fdopen(errread, 'rb', bufsize) + self.stderr = _NoINTR(os.fdopen(errread, 'rb', bufsize)) def _translate_newlines(self, data): @@ -634,7 +663,7 @@ # We didn't get to successfully create a child process. return # In case the child hasn't been waited on, check if it's done. - self._internal_poll(_deadstate=sys.maxint) + self.poll(_deadstate=sys.maxint) if self.returncode is None and _active is not None: # Child is still running, keep us alive until we can wait on it. _active.append(self) @@ -656,13 +685,13 @@ stderr = None if self.stdin: if input: - self.stdin.write(input) + _no_intr(self.stdin.write)(input) self.stdin.close() elif self.stdout: - stdout = self.stdout.read() + stdout = _no_intr(self.stdout.read)() self.stdout.close() elif self.stderr: - stderr = self.stderr.read() + stderr = _no_intr(self.stderr.read)() self.stderr.close() self.wait() return (stdout, stderr) @@ -670,10 +699,6 @@ return self._communicate(input) - def poll(self): - return self._internal_poll() - - if mswindows: # # Windows methods @@ -847,7 +872,7 @@ errwrite.Close() - def _internal_poll(self, _deadstate=None): + def poll(self, _deadstate=None): """Check if child process has terminated. Returns returncode attribute.""" if self.returncode is None: @@ -1071,7 +1096,7 @@ exc_value, tb) exc_value.child_traceback = ''.join(exc_lines) - os.write(errpipe_write, pickle.dumps(exc_value)) + _no_intr(os.write)(errpipe_write, pickle.dumps(exc_value)) # This exitcode won't be reported to applications, so it # really doesn't matter what we return. @@ -1089,10 +1114,10 @@ os.close(errwrite) # Wait for exec to fail or succeed; possibly raising exception - data = os.read(errpipe_read, 1048576) # Exceptions limited to 1 MB + data = _no_intr(os.read)(errpipe_read, 1048576) # Exceptions limited to 1 MB os.close(errpipe_read) if data != "": - os.waitpid(self.pid, 0) + _no_intr(os.waitpid)(self.pid, 0) child_exception = pickle.loads(data) raise child_exception @@ -1107,12 +1132,12 @@ raise RuntimeError("Unknown child exit status!") - def _internal_poll(self, _deadstate=None): + def poll(self, _deadstate=None): """Check if child process has terminated. Returns returncode attribute.""" if self.returncode is None: try: - pid, sts = os.waitpid(self.pid, os.WNOHANG) + pid, sts = _no_intr(os.waitpid)(self.pid, os.WNOHANG) if pid == self.pid: self._handle_exitstatus(sts) except os.error: @@ -1125,7 +1150,7 @@ """Wait for child process to terminate. Returns returncode attribute.""" if self.returncode is None: - pid, sts = os.waitpid(self.pid, 0) + pid, sts = _no_intr(os.waitpid)(self.pid, 0) self._handle_exitstatus(sts) return self.returncode @@ -1153,32 +1178,27 @@ input_offset = 0 while read_set or write_set: - try: - rlist, wlist, xlist = select.select(read_set, write_set, []) - except select.error, e: - if e.args[0] == errno.EINTR: - continue - raise + rlist, wlist, xlist = select.select(read_set, write_set, []) if self.stdin in wlist: # When select has indicated that the file is writable, # we can write up to PIPE_BUF bytes without risk # blocking. POSIX defines PIPE_BUF >= 512 - bytes_written = os.write(self.stdin.fileno(), buffer(input, input_offset, 512)) + bytes_written = _no_intr(os.write)(self.stdin.fileno(), buffer(input, input_offset, 512)) input_offset += bytes_written if input_offset >= len(input): self.stdin.close() write_set.remove(self.stdin) if self.stdout in rlist: - data = os.read(self.stdout.fileno(), 1024) + data = _no_intr(os.read)(self.stdout.fileno(), 1024) if data == "": self.stdout.close() read_set.remove(self.stdout) stdout.append(data) if self.stderr in rlist: - data = os.read(self.stderr.fileno(), 1024) + data = _no_intr(os.read)(self.stderr.fileno(), 1024) if data == "": self.stderr.close() read_set.remove(self.stderr)