rendered paste bodydiff -u python2.5-2.5/debian/changelog python2.5-2.5/debian/changelog--- python2.5-2.5/debian/changelog+++ python2.5-2.5/debian/changelog@@ -1,3 +1,16 @@+python2.5 (2.5-5ubuntu10) feisty; urgency=low++ * Add debian/patches/z_subprocess-eintr-safety.dpatch:+ - Create and use wrappers around read(), write(), and os.waitpid() in the+ subprocess module which retry the operation on an EINTR (which happens+ if e. g. an alarm was raised while the system call was in progress). It is+ incredibly hard and inconvenient to sensibly handle this in+ applications, so let's fix this at the right level.+ - Add two test cases.+ - (LP: #87292)++ -- Martin Pitt <martin.pitt@ubuntu.com> Wed, 14 Mar 2007 19:21:35 +0100+ python2.5 (2.5-5ubuntu9) feisty; urgency=medium * Update to 20070307, taken from the 2.5 release branch.only in patch2:unchanged:--- python2.5-2.5.orig/debian/patches/z_subprocess-eintr-safety.dpatch+++ python2.5-2.5/debian/patches/z_subprocess-eintr-safety.dpatch@@ -0,0 +1,231 @@+#! /bin/sh -e++dir=+if [ $# -eq 3 -a "$2" = '-d' ]; then+ pdir="-d $3"+ dir="$3/"+elif [ $# -ne 1 ]; then+ echo >&2 "usage: `basename $0`: -patch|-unpatch [-d <srcdir>]"+ exit 1+fi+case "$1" in+ -patch)+ patch $pdir -f --no-backup-if-mismatch -p0 < $0+ #cd ${dir}gcc && autoconf+ ;;+ -unpatch)+ patch $pdir -f --no-backup-if-mismatch -R -p0 < $0+ #rm ${dir}gcc/configure+ ;;+ *)+ echo >&2 "usage: `basename $0`: -patch|-unpatch [-d <srcdir>]"+ exit 1+esac+exit 0++--- orig/Lib/subprocess.py 2007-03-14 19:16:36.000000000 +0100++++ new/Lib/subprocess.py 2007-03-14 19:18:50.000000000 +0100+@@ -655,12 +655,12 @@ class Popen(object):+ stderr = None+ if self.stdin:+ if input:+- self.stdin.write(input)++ self._fo_write_no_intr(self.stdin, input)+ self.stdin.close()+ elif self.stdout:+- stdout = self.stdout.read()++ stdout = self._fo_read_no_intr(self.stdout)+ elif self.stderr:+- stderr = self.stderr.read()++ stderr = self._fo_read_no_intr(self.stderr)+ self.wait()+ return (stdout, stderr)+ +@@ -977,6 +977,72 @@ class Popen(object):+ pass+ + ++ def _read_no_intr(self, fd, buffersize):++ """Like os.read, but retries on EINTR"""++ while True:++ try:++ return os.read(fd, buffersize)++ except OSError, e:++ if e.errno == errno.EINTR:++ continue++ else:++ raise++++++ def _read_all(self, fd, buffersize):++ """Like os.read, but retries on EINTR, and reads until EOF"""++ all = ""++ while True:++ data = self._read_no_intr(fd, buffersize)++ all += data++ if data == "":++ return all++++++ def _write_no_intr(self, fd, s):++ """Like os.write, but retries on EINTR"""++ while True:++ try:++ return os.write(fd, s)++ except OSError, e:++ if e.errno == errno.EINTR:++ continue++ else:++ raise++++ def _waitpid_no_intr(self, pid, options):++ """Like os.waitpid, but retries on EINTR"""++ while True:++ try:++ return os.waitpid(pid, options)++ except OSError, e:++ if e.errno == errno.EINTR:++ continue++ else:++ raise++++ def _fo_read_no_intr(self, obj):++ """Like obj.read(), but retries on EINTR"""++ while True:++ try:++ return obj.read()++ except IOError, e:++ if e.errno == errno.EINTR:++ continue++ else:++ raise++++ def _fo_write_no_intr(self, obj, data):++ """Like obj.write(), but retries on EINTR"""++ while True:++ try:++ return obj.write(data)++ except IOError, e:++ if e.errno == errno.EINTR:++ continue++ else:++ raise+++ def _execute_child(self, args, executable, preexec_fn, close_fds,+ cwd, env, universal_newlines,+ startupinfo, creationflags, shell,+@@ -1055,7 +1121,7 @@ class Popen(object):+ exc_value,+ tb)+ exc_value.child_traceback = ''.join(exc_lines)+- os.write(errpipe_write, pickle.dumps(exc_value))++ self._write_no_intr(errpipe_write, pickle.dumps(exc_value))+ + # This exitcode won't be reported to applications, so it+ # really doesn't matter what we return.+@@ -1071,10 +1137,10 @@ class Popen(object):+ 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 = self._read_all(errpipe_read, 1048576) # Exceptions limited to 1 MB+ os.close(errpipe_read)+ if data != "":+- os.waitpid(self.pid, 0)++ self._waitpid_no_intr(self.pid, 0)+ child_exception = pickle.loads(data)+ raise child_exception+ +@@ -1094,7 +1160,7 @@ class Popen(object):+ attribute."""+ if self.returncode is None:+ try:+- pid, sts = os.waitpid(self.pid, os.WNOHANG)++ pid, sts = self._waitpid_no_intr(self.pid, os.WNOHANG)+ if pid == self.pid:+ self._handle_exitstatus(sts)+ except os.error:+@@ -1107,7 +1173,7 @@ class Popen(object):+ """Wait for child process to terminate. Returns returncode+ attribute."""+ if self.returncode is None:+- pid, sts = os.waitpid(self.pid, 0)++ pid, sts = self._waitpid_no_intr(self.pid, 0)+ self._handle_exitstatus(sts)+ return self.returncode+ +@@ -1135,27 +1201,33 @@ class Popen(object):+ + input_offset = 0+ while read_set or write_set:+- rlist, wlist, xlist = select.select(read_set, write_set, [])++ try:++ rlist, wlist, xlist = select.select(read_set, write_set, [])++ except select.error, e:++ if e[0] == errno.EINTR:++ continue++ else:++ raise+ + 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 = self._write_no_intr(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 = self._read_no_intr(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 = self._read_no_intr(self.stderr.fileno(), 1024)+ if data == "":+ self.stderr.close()+ read_set.remove(self.stderr)+--- orig/Lib/test/test_subprocess.py 2007-03-14 19:16:36.000000000 +0100++++ new/Lib/test/test_subprocess.py 2007-03-14 19:18:57.000000000 +0100+@@ -580,6 +578,34 @@ class ProcessTestCase(unittest.TestCase)+ os.remove(fname)+ self.assertEqual(rc, 47)+ ++ def test_eintr(self):++ # retries on EINTR for an argv++++ # send ourselves a signal that causes EINTR++ prev_handler = signal.signal(signal.SIGALRM, lambda x,y: 1)++ signal.alarm(1)++ time.sleep(0.5)++++ rc = subprocess.Popen(['sleep', '1'])++ self.assertEqual(rc.wait(), 0)++++ signal.signal(signal.SIGALRM, prev_handler)++++ def test_eintr_out(self):++ # retries on EINTR for a shell call and pipelining++++ # send ourselves a signal that causes EINTR++ prev_handler = signal.signal(signal.SIGALRM, lambda x,y: 1)++ signal.alarm(1)++ time.sleep(0.5)++++ rc = subprocess.Popen("sleep 1; echo hello",++ shell=True, stdout=subprocess.PIPE)++ out = rc.communicate()[0]++ self.assertEqual(rc.returncode, 0)++ self.assertEqual(out, "hello\n")++++ signal.signal(signal.SIGALRM, prev_handler)+ + #+ # Windows tests