]> git.ipfire.org Git - thirdparty/dnspython.git/commitdiff
add poll() support; bump version to 1.9.0
authorBob Halley <halley@nominum.com>
Wed, 27 Oct 2010 20:30:43 +0000 (21:30 +0100)
committerBob Halley <halley@nominum.com>
Wed, 27 Oct 2010 20:30:43 +0000 (21:30 +0100)
ChangeLog
README
dns/query.py
dns/version.py
tests/resolver.py

index 5a90235ae29cd927bd278c96e482027bb02dbce3..d1620982ef40ac7d1354d95f493a53115c1d9f4b 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,9 @@
+2010-10-27  Bob Halley  <halley@dnspython.org>
+
+       * Incorporate a patch to use poll() instead of select() by
+         default on platforms which support it.  Thanks to
+         Peter Schüller and Spotify for the contribution.
+
 2010-10-17  Bob Halley  <halley@dnspython.org>
 
        * Python prior to 2.5.2 doesn't compute the correct values for
diff --git a/README b/README
index b313d1c132e6852ab275a6fb474c6de73693748d..72e3e15fc2d0b6809ef3e019f96c3ca2adb5a306 100644 (file)
--- a/README
+++ b/README
@@ -22,7 +22,15 @@ development by continuing to employ the author :).
 
 ABOUT THIS RELEASE
 
-This is dnspython 1.8.0
+This is dnspython 1.9.0
+
+New since 1.8.0:
+
+       dnspython now uses poll() instead of select() when available.
+
+Bugs fixed since 1.8.0
+
+       XXX TBS XXX
 
 New since 1.7.1:
 
index 4d8379ee9cfc5608e908c9fec99496b614cd2c2e..9dc88a635c4bb82771effabbbb6a946cf2f8b2c0 100644 (file)
@@ -45,7 +45,59 @@ def _compute_expiration(timeout):
     else:
         return time.time() + timeout
 
-def _wait_for(ir, iw, ix, expiration):
+def _poll_for(fd, readable, writable, error, timeout):
+    """
+    @param fd: File descriptor (int).
+    @param readable: Whether to wait for readability (bool).
+    @param writable: Whether to wait for writability (bool).
+    @param expiration: Deadline timeout (expiration time, in seconds (float)).
+
+    @return True on success, False on timeout
+    """
+    event_mask = 0
+    if readable:
+        event_mask |= select.POLLIN
+    if writable:
+        event_mask |= select.POLLOUT
+    if error:
+        event_mask |= select.POLLERR
+
+    pollable = select.poll()
+    pollable.register(fd, event_mask)
+
+    if timeout:
+        event_list = pollable.poll(long(timeout * 1000))
+    else:
+        event_list = pollable.poll()
+
+    return bool(event_list)
+
+def _select_for(fd, readable, writable, error, timeout):
+    """
+    @param fd: File descriptor (int).
+    @param readable: Whether to wait for readability (bool).
+    @param writable: Whether to wait for writability (bool).
+    @param expiration: Deadline timeout (expiration time, in seconds (float)).
+
+    @return True on success, False on timeout
+    """
+    rset, wset, xset = [], [], []
+
+    if readable:
+        rset = [fd]
+    if writable:
+        wset = [fd]
+    if error:
+        xset = [fd]
+
+    if timeout is None:
+        (rcount, wcount, xcount) = select.select(rset, wset, xset)
+    else:
+        (rcount, wcount, xcount) = select.select(rset, wset, xset, timeout)
+
+    return bool((rcount or wcount or xcount))
+
+def _wait_for(fd, readable, writable, error, expiration):
     done = False
     while not done:
         if expiration is None:
@@ -55,22 +107,34 @@ def _wait_for(ir, iw, ix, expiration):
             if timeout <= 0.0:
                 raise dns.exception.Timeout
         try:
-            if timeout is None:
-                (r, w, x) = select.select(ir, iw, ix)
-            else:
-                (r, w, x) = select.select(ir, iw, ix, timeout)
+            if not _polling_backend(fd, readable, writable, error, timeout):
+                raise dns.exception.Timeout
         except select.error, e:
             if e.args[0] != errno.EINTR:
                 raise e
         done = True
-        if len(r) == 0 and len(w) == 0 and len(x) == 0:
-            raise dns.exception.Timeout
+
+def _set_polling_backend(fn):
+    """
+    Internal API. Do not use.
+    """
+    global _polling_backend
+
+    _polling_backend = fn
+
+if hasattr(select, 'poll'):
+    # Prefer poll() on platforms that support it because it has no
+    # limits on the maximum value of a file descriptor (plus it will
+    # be more efficient for high values).
+    _polling_backend = _poll_for
+else:
+    _polling_backend = _select_for
 
 def _wait_for_readable(s, expiration):
-    _wait_for([s], [], [s], expiration)
+    _wait_for(s, True, False, True, expiration)
 
 def _wait_for_writable(s, expiration):
-    _wait_for([], [s], [s], expiration)
+    _wait_for(s, False, True, True, expiration)
 
 def _addresses_equal(af, a1, a2):
     # Convert the first value of the tuple, which is a textual format
index dd135a13e520b952b784b4ffb1f3e0d7b9623a01..251079f4cfa99123e20cac2d485b5eeff1969ee2 100644 (file)
@@ -16,8 +16,8 @@
 """dnspython release version information."""
 
 MAJOR = 1
-MINOR = 8
-MICRO = 1
+MINOR = 9
+MICRO = 0
 RELEASELEVEL = 0x0f
 SERIAL = 0
 
index 4cacbdc79d95e747448dcb08def95bca3edf54d1..bd6dc5fbc2725f430a8df5b1157004863bf38daf 100644 (file)
@@ -14,6 +14,7 @@
 # OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 
 import cStringIO
+import select
 import sys
 import time
 import unittest
@@ -46,7 +47,7 @@ example. 1 IN A 10.0.0.1
 ;ADDITIONAL
 """
 
-class ResolverTestCase(unittest.TestCase):
+class BaseResolverTests(object):
 
     if sys.platform != 'win32':
         def testRead(self):
@@ -101,5 +102,26 @@ class ResolverTestCase(unittest.TestCase):
             zname = dns.resolver.zone_for_name(name)
         self.failUnlessRaises(dns.resolver.NotAbsolute, bad)
 
+class PollingMonkeyPatchMixin(object):
+    def setUp(self):
+        self.__native_polling_backend = dns.query._polling_backend
+        dns.query._set_polling_backend(self.polling_backend())
+
+        unittest.TestCase.setUp(self)
+
+    def tearDown(self):
+        dns.query._set_polling_backend(self.__native_polling_backend)
+
+        unittest.TestCase.tearDown(self)
+
+class SelectResolverTestCase(PollingMonkeyPatchMixin, BaseResolverTests, unittest.TestCase):
+    def polling_backend(self):
+        return dns.query._select_for
+
+if hasattr(select, 'poll'):
+    class PollResolverTestCase(PollingMonkeyPatchMixin, BaseResolverTests, unittest.TestCase):
+        def polling_backend(self):
+            return dns.query._poll_for
+
 if __name__ == '__main__':
     unittest.main()