[Jython-checkins] jython: select.select and related socket.connect_ex fixes

jim.baker jython-checkins at python.org
Mon Dec 29 08:00:07 CET 2014


https://hg.python.org/jython/rev/3c4adde9c083
changeset:   7475:3c4adde9c083
user:        Jim Baker <jim.baker at rackspace.com>
date:        Mon Dec 29 00:00:00 2014 -0700
summary:
  select.select and related socket.connect_ex fixes

Fixed child socket handling such that if the parent socket is
blocking, the child socket will now immediately complete its setup.

Fixed socket.connect_ex so that it depends on the underlying connect
future, as well as catching any socket.error exceptions and returning
the corresponding errno. This change enables test_select_new.

Fixes http://bugs.jython.org/issue2242

files:
  Lib/_socket.py              |  24 ++++++++++++++++--------
  Lib/test/regrtest.py        |   1 -
  Lib/test/test_select_new.py |  18 +++++++++---------
  Lib/test/test_socket.py     |  15 ++++++++++++++-
  4 files changed, 39 insertions(+), 19 deletions(-)


diff --git a/Lib/_socket.py b/Lib/_socket.py
--- a/Lib/_socket.py
+++ b/Lib/_socket.py
@@ -615,6 +615,8 @@
         # thread pool
         child_channel.closeFuture().addListener(unlatch_child)
 
+        if self.parent_socket.timeout is None:
+            child._ensure_post_connect()
         child._wait_on_latch()
         log.debug("Socket initChannel completed waiting on latch", extra={"sock": child})
 
@@ -842,8 +844,8 @@
             log.debug("Connect to %s", addr, extra={"sock": self})
             self.channel = bootstrap.channel()
 
-        connect_future = self.channel.connect(addr)
-        self._handle_channel_future(connect_future, "connect")
+        self.connect_future = self.channel.connect(addr)
+        self._handle_channel_future(self.connect_future, "connect")
         self.bind_timestamp = time.time()
 
     def _post_connect(self):
@@ -871,12 +873,17 @@
             log.debug("Completed connection to %s", addr, extra={"sock": self})
 
     def connect_ex(self, addr):
-        self.connect(addr)
-        if self.timeout is None:
+        if not self.connected:
+            try:
+                self.connect(addr)
+            except error as e:
+                return e.errno
+        if not self.connect_future.isDone():
+            return errno.EINPROGRESS
+        elif self.connect_future.isSuccess():
             return errno.EISCONN
         else:
-            return errno.EINPROGRESS
-
+            return errno.ENOTCONN
 
     # SERVER METHODS
     # Calling listen means this is a server socket
@@ -1033,11 +1040,11 @@
                 pass  # already removed, can safely ignore (presumably)
         if how & SHUT_WR:
             self._can_write = False
-            
+
     def _readable(self):
         if self.socket_type == CLIENT_SOCKET or self.socket_type == DATAGRAM_SOCKET:
             log.debug("Incoming head=%s queue=%s", self.incoming_head, self.incoming, extra={"sock": self})
-            return (
+            return bool(
                 (self.incoming_head is not None and self.incoming_head.readableBytes()) or
                 self.incoming.peek())
         elif self.socket_type == SERVER_SOCKET:
@@ -1338,6 +1345,7 @@
         self.active = AtomicBoolean()
         self.active_latch = CountDownLatch(1)
         self.accepted = False
+        self.timeout = parent_socket.timeout
 
     def _ensure_post_connect(self):
         do_post_connect = not self.active.getAndSet(True)
diff --git a/Lib/test/regrtest.py b/Lib/test/regrtest.py
--- a/Lib/test/regrtest.py
+++ b/Lib/test/regrtest.py
@@ -1314,7 +1314,6 @@
         test_peepholer
         test_pyclbr
         test_pyexpat
-        test_select_new
         test_stringprep
         test_threadsignals
         test_transformer
diff --git a/Lib/test/test_select_new.py b/Lib/test/test_select_new.py
--- a/Lib/test/test_select_new.py
+++ b/Lib/test/test_select_new.py
@@ -16,18 +16,15 @@
 DATA_CHUNK = "." * DATA_CHUNK_SIZE
 
 #
-# The timing of these tests depends on the how the unerlying OS socket library
+# The timing of these tests depends on the how the underlying OS socket library
 # handles buffering. These values may need tweaking for different platforms
 #
 # The fundamental problem is that there is no reliable way to fill a socket with bytes
-#
+# To address this for running on Netty, we arbitrarily send 10000 bytes
 
-if test_support.is_jython:
-    SELECT_TIMEOUT = 0
-else:
-    # zero select timeout fails these tests on cpython (on windows 2003 anyway)
-    SELECT_TIMEOUT = 0.001
-
+# zero select timeout fails these tests on cpython (on windows 2003 anyway);
+# on Jython with Netty it will result in flaky test runs
+SELECT_TIMEOUT = 0.001
 READ_TIMEOUT = 5
 
 class AsynchronousServer:
@@ -86,6 +83,9 @@
                 if self.select_writable():
                     bytes_sent = self.socket.send(DATA_CHUNK)
                     total_bytes += bytes_sent
+                    if test_support.is_jython and total_bytes > 10000:
+                        # Netty will buffer indefinitely, so just pick an arbitrary cutoff
+                        return total_bytes
                 else:
                     return total_bytes
             except socket.error, se:
@@ -149,7 +149,7 @@
     def start_connect(self):
         result = self.socket.connect_ex(SERVER_ADDRESS)
         if result == errno.EISCONN:
-            self.connected = 1
+            self.connected = True
         else:
             assert result == errno.EINPROGRESS
 
diff --git a/Lib/test/test_socket.py b/Lib/test/test_socket.py
--- a/Lib/test/test_socket.py
+++ b/Lib/test/test_socket.py
@@ -1125,6 +1125,20 @@
         self.serv_conn.send(MSG)
         self.serv_conn.send('and ' + MSG)
 
+    def testSelect(self):
+        # http://bugs.jython.org/issue2242
+        self.assertIs(self.cli_conn.gettimeout(), None, "Server socket is not blocking")
+        start_time = time.time()
+        r, w, x = select.select([self.cli_conn], [], [], 10)
+        if (time.time() - start_time) > 1:
+            self.fail("Child socket was not immediately available for read when set to blocking")
+        self.assertEqual(r[0], self.cli_conn)
+        self.assertEqual(self.cli_conn.recv(1024), MSG)
+
+    def _testSelect(self):
+        self.serv_conn.send(MSG)
+
+
 class UDPBindTest(unittest.TestCase):
 
     HOST = HOST
@@ -1396,7 +1410,6 @@
     def _testRecvData(self):
         self.cli.connect((self.HOST, self.PORT))
         self.cli.send(MSG)
-        #time.sleep(0.5)
 
     def testRecvNoData(self):
         # Testing non-blocking recv

-- 
Repository URL: https://hg.python.org/jython


More information about the Jython-checkins mailing list