Merged revisions 74426 via svnmerge from
svn+ssh://pythondev@svn.python.org/python/trunk

........
  r74426 | gregory.p.smith | 2009-08-13 11:54:50 -0700 (Thu, 13 Aug 2009) | 4 lines

  Fix issue1628205: Socket file objects returned by socket.socket.makefile() now
  properly handles EINTR within the read, readline, write & flush methods.
  The socket.sendall() method now properly handles interrupted system calls.
........
diff --git a/Lib/test/test_socket.py b/Lib/test/test_socket.py
index 09e2cc8..4326e65 100644
--- a/Lib/test/test_socket.py
+++ b/Lib/test/test_socket.py
@@ -4,6 +4,7 @@
 from test import support
 
 import errno
+import io
 import socket
 import select
 import _thread as thread
@@ -906,6 +907,117 @@
         pass
 
 
+class FileObjectInterruptedTestCase(unittest.TestCase):
+    """Test that the file object correctly handles EINTR internally."""
+
+    class MockSocket(object):
+        def __init__(self, recv_funcs=()):
+            # A generator that returns callables that we'll call for each
+            # call to recv().
+            self._recv_step = iter(recv_funcs)
+
+        def recv_into(self, buffer):
+            data = next(self._recv_step)()
+            assert len(buffer) >= len(data)
+            buffer[:len(data)] = data
+            return len(data)
+
+        def _decref_socketios(self):
+            pass
+
+        def _textiowrap_for_test(self, buffering=-1):
+            raw = socket.SocketIO(self, "r")
+            if buffering < 0:
+                buffering = io.DEFAULT_BUFFER_SIZE
+            if buffering == 0:
+                return raw
+            buffer = io.BufferedReader(raw, buffering)
+            text = io.TextIOWrapper(buffer, None, None)
+            text.mode = "rb"
+            return text
+
+    @staticmethod
+    def _raise_eintr():
+        raise socket.error(errno.EINTR)
+
+    def _textiowrap_mock_socket(self, mock, buffering=-1):
+        raw = socket.SocketIO(mock, "r")
+        if buffering < 0:
+            buffering = io.DEFAULT_BUFFER_SIZE
+        if buffering == 0:
+            return raw
+        buffer = io.BufferedReader(raw, buffering)
+        text = io.TextIOWrapper(buffer, None, None)
+        text.mode = "rb"
+        return text
+
+    def _test_readline(self, size=-1, buffering=-1):
+        mock_sock = self.MockSocket(recv_funcs=[
+                lambda : b"This is the first line\nAnd the sec",
+                self._raise_eintr,
+                lambda : b"ond line is here\n",
+                lambda : b"",
+                lambda : b"",  # XXX(gps): io library does an extra EOF read
+            ])
+        fo = mock_sock._textiowrap_for_test(buffering=buffering)
+        self.assertEquals(fo.readline(size), "This is the first line\n")
+        self.assertEquals(fo.readline(size), "And the second line is here\n")
+
+    def _test_read(self, size=-1, buffering=-1):
+        mock_sock = self.MockSocket(recv_funcs=[
+                lambda : b"This is the first line\nAnd the sec",
+                self._raise_eintr,
+                lambda : b"ond line is here\n",
+                lambda : b"",
+                lambda : b"",  # XXX(gps): io library does an extra EOF read
+            ])
+        expecting = (b"This is the first line\n"
+                     b"And the second line is here\n")
+        fo = mock_sock._textiowrap_for_test(buffering=buffering)
+        if buffering == 0:
+            data = b''
+        else:
+            data = ''
+            expecting = expecting.decode('utf8')
+        while len(data) != len(expecting):
+            part = fo.read(size)
+            if not part:
+                break
+            data += part
+        self.assertEquals(data, expecting)
+
+    def test_default(self):
+        self._test_readline()
+        self._test_readline(size=100)
+        self._test_read()
+        self._test_read(size=100)
+
+    def test_with_1k_buffer(self):
+        self._test_readline(buffering=1024)
+        self._test_readline(size=100, buffering=1024)
+        self._test_read(buffering=1024)
+        self._test_read(size=100, buffering=1024)
+
+    def _test_readline_no_buffer(self, size=-1):
+        mock_sock = self.MockSocket(recv_funcs=[
+                lambda : b"a",
+                lambda : b"\n",
+                lambda : b"B",
+                self._raise_eintr,
+                lambda : b"b",
+                lambda : b"",
+            ])
+        fo = mock_sock._textiowrap_for_test(buffering=0)
+        self.assertEquals(fo.readline(size), b"a\n")
+        self.assertEquals(fo.readline(size), b"Bb")
+
+    def test_no_buffer(self):
+        self._test_readline_no_buffer()
+        self._test_readline_no_buffer(size=4)
+        self._test_read(buffering=0)
+        self._test_read(size=100, buffering=0)
+
+
 class UnbufferedFileObjectClassTestCase(FileObjectClassTestCase):
 
     """Repeat the tests from FileObjectClassTestCase with bufsize==0.
@@ -1310,6 +1422,7 @@
     tests.extend([
         NonBlockingTCPTests,
         FileObjectClassTestCase,
+        FileObjectInterruptedTestCase,
         UnbufferedFileObjectClassTestCase,
         LineBufferedFileObjectClassTestCase,
         SmallBufferedFileObjectClassTestCase,