Index: Misc/NEWS =================================================================== --- Misc/NEWS (revision 87589) +++ Misc/NEWS (working copy) @@ -20,6 +20,9 @@ Library ------- +- Issue 7322: File object from socket.makefile() now raises IOError if a socket + timeout is set when trying to read or write. Patch by Ross Lagerwall. + - Issue 10786: unittest.TextTestRunner default stream no longer bound at import time. `sys.stderr` now looked up at instantiation time. Fix contributed by Mark Roddy. Index: Doc/library/socket.rst =================================================================== --- Doc/library/socket.rst (revision 87589) +++ Doc/library/socket.rst (working copy) @@ -766,8 +766,8 @@ timeout modes are shared between file descriptors and socket objects that refer to the same network endpoint. A consequence of this is that file objects returned by the :meth:`~socket.makefile` method must only be used when the -socket is in blocking mode; in timeout or non-blocking mode file operations -that cannot be completed immediately will fail. +socket is in blocking mode; in timeout or non-blocking mode, file operations +raise :exc:`IOError`. Note that the :meth:`~socket.connect` operation is subject to the timeout setting, and in general it is recommended to call :meth:`~socket.settimeout` Index: Lib/socket.py =================================================================== --- Lib/socket.py (revision 87589) +++ Lib/socket.py (working copy) @@ -254,14 +254,16 @@ def readinto(self, b): """Read up to len(b) bytes into the writable buffer *b* and return - the number of bytes read. If the socket is non-blocking and no bytes - are available, None is returned. + the number of bytes read. If the socket is non-blocking, IOError + is raised. If *b* is non-empty, a 0 return value indicates that the connection was shutdown at the other end. """ self._checkClosed() self._checkReadable() + if self._sock.gettimeout() != None: + raise IOError("socket timeout is set") while True: try: return self._sock.recv_into(b) @@ -277,10 +279,12 @@ """Write the given bytes or bytearray object *b* to the socket and return the number of bytes written. This can be less than len(b) if not all data could be written. If the socket is - non-blocking and no bytes could be written None is returned. + non-blocking, IOError is raised. """ self._checkClosed() self._checkWritable() + if self._sock.gettimeout() != None: + raise IOError("socket timeout is set") try: return self._sock.send(b) except error as e: Index: Lib/test/test_socket.py =================================================================== --- Lib/test/test_socket.py (revision 87589) +++ Lib/test/test_socket.py (working copy) @@ -1184,6 +1184,9 @@ # call to recv(). self._recv_step = iter(recv_funcs) + def gettimeout(self): + return None + def recv_into(self, buffer): data = next(self._recv_step)() assert len(buffer) >= len(data) @@ -1339,57 +1342,19 @@ def testSmallReadNonBlocking(self): self.cli_conn.setblocking(False) - self.assertEqual(self.read_file.readinto(bytearray(10)), None) - self.assertEqual(self.read_file.read(len(self.read_msg) - 3), None) - self.evt1.set() - self.evt2.wait(1.0) - first_seg = self.read_file.read(len(self.read_msg) - 3) - buf = bytearray(10) - n = self.read_file.readinto(buf) - self.assertEqual(n, 3) - msg = first_seg + buf[:n] - self.assertEqual(msg, self.read_msg) - self.assertEqual(self.read_file.readinto(bytearray(16)), None) - self.assertEqual(self.read_file.read(1), None) + self.assertRaises(IOError, self.read_file.readline) def _testSmallReadNonBlocking(self): - self.evt1.wait(1.0) - self.write_file.write(self.write_msg) - self.write_file.flush() - self.evt2.set() - # Avoid cloding the socket before the server test has finished, - # otherwise system recv() will return 0 instead of EWOULDBLOCK. - self.serv_finished.wait(5.0) + pass def testWriteNonBlocking(self): - self.cli_finished.wait(5.0) - # The client thread can't skip directly - the SkipTest exception - # would appear as a failure. - if self.serv_skipped: - self.skipTest(self.serv_skipped) + pass def _testWriteNonBlocking(self): - self.serv_skipped = None self.serv_conn.setblocking(False) - # Try to saturate the socket buffer pipe with repeated large writes. BIG = b"x" * (1024 ** 2) - LIMIT = 10 - # The first write() succeeds since a chunk of data can be buffered - n = self.write_file.write(BIG) - self.assertGreater(n, 0) - for i in range(LIMIT): - n = self.write_file.write(BIG) - if n is None: - # Succeeded - break - self.assertGreater(n, 0) - else: - # Let us know that this test didn't manage to establish - # the expected conditions. This is not a failure in itself but, - # if it happens repeatedly, the test should be fixed. - self.serv_skipped = "failed to saturate the socket buffer" + self.assertRaises(IOError, self.write_file.write, BIG) - class LineBufferedFileObjectClassTestCase(FileObjectClassTestCase): bufsize = 1 # Default-buffered for reading; line-buffered for writing