]> granicus.if.org Git - python/commitdiff
Issue #16133: The asynchat.async_chat.handle_read() method now ignores
authorVictor Stinner <victor.stinner@gmail.com>
Thu, 24 Jul 2014 17:15:00 +0000 (19:15 +0200)
committerVictor Stinner <victor.stinner@gmail.com>
Thu, 24 Jul 2014 17:15:00 +0000 (19:15 +0200)
socket.error() exceptions with blocking I/O errors: EAGAIN, EALREADY,
EINPROGRESS, or EWOULDBLOCK. Initial patch written by Xavier de Gaye.

Doc/library/asyncore.rst
Lib/asynchat.py
Lib/test/test_asynchat.py
Misc/NEWS

index 70ca8e781f2aa52a1a67ff4695ab52b5ada06b93..31cd497f59c9ccc0bf422ec0ad4625e531af6597 100644 (file)
@@ -193,6 +193,11 @@ any that have been added to the map during asynchronous service) is closed.
       Read at most *buffer_size* bytes from the socket's remote end-point.  An
       empty string implies that the channel has been closed from the other end.
 
+      Note that :meth:`recv` may raise :exc:`socket.error` with
+      :data:`~errno.EAGAIN` or :data:`~errno.EWOULDBLOCK`, even though
+      :func:`select.select` or :func:`select.poll` has reported the socket
+      ready for reading.
+
 
    .. method:: listen(backlog)
 
index 911833d58ced7a779cb8f93aaec4d42556820023..57459a0821b41e58eac78c736264e0a3d985c70f 100644 (file)
@@ -46,12 +46,17 @@ method) up to the terminator, and then control will be returned to
 you - by calling your self.found_terminator() method.
 """
 
-import socket
 import asyncore
+import errno
+import socket
 from collections import deque
 from sys import py3kwarning
 from warnings import filterwarnings, catch_warnings
 
+_BLOCKING_IO_ERRORS = (errno.EAGAIN, errno.EALREADY, errno.EINPROGRESS,
+                       errno.EWOULDBLOCK)
+
+
 class async_chat (asyncore.dispatcher):
     """This is an abstract class.  You must derive from this class, and add
     the two methods collect_incoming_data() and found_terminator()"""
@@ -109,6 +114,8 @@ class async_chat (asyncore.dispatcher):
         try:
             data = self.recv (self.ac_in_buffer_size)
         except socket.error, why:
+            if why.args[0] in _BLOCKING_IO_ERRORS:
+                return
             self.handle_error()
             return
 
index c81ec0de45093c132bb6669cb60e4074fe6af690..9c4c71448c965960182c45c84665197d30f82173 100644 (file)
@@ -1,6 +1,10 @@
 # test asynchat
 
-import asyncore, asynchat, socket, time
+import errno
+import asyncore
+import asynchat
+import socket
+import time
 import unittest
 import sys
 from test import test_support
@@ -235,6 +239,31 @@ class TestAsynchat(unittest.TestCase):
 class TestAsynchat_WithPoll(TestAsynchat):
     usepoll = True
 
+
+class TestAsynchatMocked(unittest.TestCase):
+    def test_blockingioerror(self):
+        # Issue #16133: handle_read() must ignore blocking I/O errors like
+        # EAGAIN
+        class fake_socket:
+            def fileno(self):
+                return 0
+
+            def recv(self, size):
+                raise socket.error(errno.EAGAIN, "EAGAIN")
+
+        class MyChat(asynchat.async_chat):
+            def handle_error(self):
+                raise Exception("error")
+
+        sock = fake_socket()
+        dispatcher = MyChat()
+        dispatcher.set_socket(sock)
+        self.addCleanup(dispatcher.del_channel)
+
+        # must not call handle_error()
+        dispatcher.handle_read()
+
+
 class TestHelperFunctions(unittest.TestCase):
     def test_find_prefix_at_end(self):
         self.assertEqual(asynchat.find_prefix_at_end("qwerty\r", "\r\n"), 1)
@@ -267,6 +296,7 @@ class TestFifo(unittest.TestCase):
 
 def test_main(verbose=None):
     test_support.run_unittest(TestAsynchat, TestAsynchat_WithPoll,
+                              TestAsynchatMocked,
                               TestHelperFunctions, TestFifo)
 
 if __name__ == "__main__":
index f1c9ab32980f2b79b59f7e38c643df50db8e7873..4cfa3c2af21417d43eee7563778d172434468e2a 100644 (file)
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -13,6 +13,10 @@ Core and Builtins
 Library
 -------
 
+- Issue #16133: The asynchat.async_chat.handle_read() method now ignores
+  socket.error() exceptions with blocking I/O errors: EAGAIN, EALREADY,
+  EINPROGRESS, or EWOULDBLOCK.
+
 - Issue #1730136: Fix the comparison between a tkFont.Font and an object of
   another kind.