[Python-checkins] r61722 - in python/trunk: Doc/library/select.rst Doc/license.rst Lib/test/regrtest.py Lib/test/test_epoll.py Lib/test/test_kqueue.py Lib/test/test_poll.py Misc/NEWS Modules/selectmodule.c configure configure.in pyconfig.h.in

christian.heimes python-checkins at python.org
Sat Mar 22 00:49:45 CET 2008


Author: christian.heimes
Date: Sat Mar 22 00:49:44 2008
New Revision: 61722

Added:
   python/trunk/Lib/test/test_epoll.py   (contents, props changed)
   python/trunk/Lib/test/test_kqueue.py   (contents, props changed)
Modified:
   python/trunk/Doc/library/select.rst
   python/trunk/Doc/license.rst
   python/trunk/Lib/test/regrtest.py
   python/trunk/Lib/test/test_poll.py
   python/trunk/Misc/NEWS
   python/trunk/Modules/selectmodule.c
   python/trunk/configure
   python/trunk/configure.in
   python/trunk/pyconfig.h.in
Log:
Applied patch #1657 epoll and kqueue wrappers for the select module
The patch adds wrappers for the Linux epoll syscalls and the BSD kqueue syscalls. Thanks to Thomas Herve and the Twisted people for their support and help.
TODO: Finish documentation documentation

Modified: python/trunk/Doc/library/select.rst
==============================================================================
--- python/trunk/Doc/library/select.rst	(original)
+++ python/trunk/Doc/library/select.rst	Sat Mar 22 00:49:44 2008
@@ -7,10 +7,12 @@
 
 
 This module provides access to the :cfunc:`select` and :cfunc:`poll` functions
-available in most operating systems.  Note that on Windows, it only works for
-sockets; on other operating systems, it also works for other file types (in
-particular, on Unix, it works on pipes).  It cannot be used on regular files to
-determine whether a file has grown since it was last read.
+available in most operating systems, :cfunc:`epoll` available on Linux 2.5+ and
+:cfunc:`kqueue` available on most BSD.
+Note that on Windows, it only works for sockets; on other operating systems,
+it also works for other file types (in particular, on Unix, it works on pipes).
+It cannot be used on regular files to determine whether a file has grown since
+it was last read.
 
 The module defines the following:
 
@@ -22,6 +24,16 @@
    string, as would be printed by the C function :cfunc:`perror`.
 
 
+.. type:: epoll([sizehint=-1])
+
+   (Only supported on Linux 2.5.44 and newer.)  Returns an edge polling
+   object, which can be used as Edge or Level Triggered interface for I/O
+   events; see section :ref:`epoll-objects` below for the methods supported
+   by epolling objects.
+
+   .. versionadded:: 2.6
+
+
 .. function:: poll()
 
    (Not supported by all operating systems.)  Returns a polling object, which
@@ -30,6 +42,24 @@
    by polling objects.
 
 
+.. type:: kqueue()
+
+   (Only supported on BSD.)  Returns a kernel queue object
+   object; see section :ref:`kqueue-objects` below for the methods supported
+   by kqueue objects.
+
+   .. versionadded:: 2.6
+
+
+.. type:: kqueue(ident, filter=KQ_FILTER_READ, flags=KQ_ADD, fflags=0, data=0, udata=0)
+
+   (Only supported on BSD.)  Returns a kernel event object
+   object; see section :ref:`kevent-objects` below for the methods supported
+   by kqueue objects.
+
+   .. versionadded:: 2.6
+
+
 .. function:: select(iwtd, owtd, ewtd[, timeout])
 
    This is a straightforward interface to the Unix :cfunc:`select` system call.
@@ -67,6 +97,81 @@
       not handle file descriptors that don't originate from WinSock.
 
 
+.. _epoll-objects:
+
+Edge and Level Trigger Polling (epoll) Objects
+----------------------------------------------
+
+   http://linux.die.net/man/4/epoll
+
+   *eventmask*
+
+   +-----------------------+-----------------------------------------------+
+   | Constant              | Meaning                                       |
+   +=======================+===============================================+
+   | :const:`EPOLLIN`      | Available for read                            |
+   +-----------------------+-----------------------------------------------+
+   | :const:`EPOLLOUT`     | Available for write                           |
+   +-----------------------+-----------------------------------------------+
+   | :const:`EPOLLPRI`     | Urgent data for read                          |
+   +-----------------------+-----------------------------------------------+
+   | :const:`EPOLLERR`     | Error condition happend on the assoc. fd      |
+   +-----------------------+-----------------------------------------------+
+   | :const:`EPOLLHUP`     | Hang up happend on the assoc. fd              |
+   +-----------------------+-----------------------------------------------+
+   | :const:`EPOLLET`      | Set Edge Trigger behavior, the default is     |
+   |                       | Level Trigger behavior                        |
+   +-----------------------+-----------------------------------------------+
+   | :const:`EPOLLONESHOT` | Set one-shot behavior. After one event is     |
+   |                       | pulled out, the fd is internally disabled     |
+   +-----------------------+-----------------------------------------------+
+   | :const:`EPOLLRDNORM`  | ???                                           |
+   +-----------------------+-----------------------------------------------+
+   | :const:`EPOLLRDBAND`  | ???                                           |
+   +-----------------------+-----------------------------------------------+
+   | :const:`EPOLLWRNORM`  | ???                                           |
+   +-----------------------+-----------------------------------------------+
+   | :const:`EPOLLWRBAND`  | ???                                           |
+   +-----------------------+-----------------------------------------------+
+   | :const:`EPOLLMSG`     | ???                                           |
+   +-----------------------+-----------------------------------------------+
+
+
+.. method:: epoll.close()
+
+   Close the control file descriptor of the epoll object.
+
+
+.. method:: epoll.fileno()
+
+   Return the file descriptor number of the control fd.
+
+
+.. method:: epoll.fromfd(fd)
+
+   Create an epoll object from a given file descriptor.
+
+
+.. method:: epoll.register(fd[, eventmask])
+
+   Register a fd descriptor with the epoll object.
+
+
+.. method:: epoll.modify(fd, eventmask)
+
+   Modify a register file descriptor.
+
+
+.. method:: epoll.unregister(fd)
+
+   Remove a registered file descriptor from the epoll object.
+
+
+.. method:: epoll.poll([timeout=-1[, maxevents=-1]])
+
+   Wait for events. timeout in seconds (float)
+
+
 .. _poll-objects:
 
 Polling Objects
@@ -114,6 +219,16 @@
    the same effect as registering the descriptor exactly once.
 
 
+.. method:: poll.modify(fd, eventmask)
+
+   Modifies an already registered fd. This has the same effect as
+   :meth:`register(fd, eventmask)`.  Attempting to modify a file descriptor
+   that was never registered causes an :exc:`IOError` exception with errno
+   :const:`ENOENT` to be raised.
+
+   .. versionadded:: 2.6
+
+
 .. method:: poll.unregister(fd)
 
    Remove a file descriptor being tracked by a polling object.  Just like the
@@ -137,3 +252,184 @@
    returning. If *timeout* is omitted, negative, or :const:`None`, the call will
    block until there is an event for this poll object.
 
+
+.. _kqueue-objects:
+
+Kqueue Objects
+--------------
+
+.. method:: kqueue.close()
+
+   Close the control file descriptor of the kqueue object.
+
+
+.. method:: kqueue.fileno()
+
+   Return the file descriptor number of the control fd.
+
+
+.. method:: epoll.fromfd(fd)
+
+   Create a kqueue object from a given file descriptor.
+
+
+.. method:: control(changelist, max_events=0[, timeout=None]) -> eventlist
+
+   Low level interface to kevent
+
+   - changelist must be an iterable of kevent object or None
+   - max_events must be 0 or a positive integer
+   - timeout in seconds (floats possible)
+
+
+.. _kevent-objects:
+
+Kevent Objects
+--------------
+
+   http://www.freebsd.org/cgi/man.cgi?query=kqueue&sektion=2
+
+   .. attribute:: ident
+
+   Value used to identify the event. The interpretation depends on the filter
+   but it's usually the file descriptor. In the constructor ident can either
+   be an int or an object with a fileno() function. kevent stores the integer
+   internally.
+
+   .. attribute:: filter
+
+   Name of the kernel filter
+
+   +---------------------------+---------------------------------------------+
+   | Constant                  | Meaning                                     |
+   +===========================+=============================================+
+   | :const:`KQ_FILTER_READ`   | Takes a descriptor and returns whenever     |
+   |                           | there is data available to read             |
+   +---------------------------+---------------------------------------------+
+   | :const:`KQ_FILTER_WRITE`  | Takes a descriptor and returns whenever     |
+   |                           | there is data available to read             |
+   +---------------------------+---------------------------------------------+
+   | :const:`KQ_FILTER_AIO`    | AIO requests                                |
+   +---------------------------+---------------------------------------------+
+   | :const:`KQ_FILTER_VNODE`  | Returns when one or more of the requested   |
+   |                           | events watched in *fflag* occurs            |
+   +---------------------------+---------------------------------------------+
+   | :const:`KQ_FILTER_PROC`   | Watch for events on a process id            |
+   +---------------------------+---------------------------------------------+
+   | :const:`KQ_FILTER_NETDEV` | Watch for events on a network device        |
+   |                           | [not available on Mac OS X]                 |
+   +---------------------------+---------------------------------------------+
+   | :const:`KQ_FILTER_SIGNAL` | Returns whenever the watched signal is      |
+   |                           | delivered to the process                    |
+   +---------------------------+---------------------------------------------+
+   | :const:`KQ_FILTER_TIMER`  | Establishes an arbitrary timer              |
+   +---------------------------+---------------------------------------------+
+
+   .. attribute:: flags
+
+   Filter action
+
+   +---------------------------+---------------------------------------------+
+   | Constant                  | Meaning                                     |
+   +===========================+=============================================+
+   | :const:`KQ_EV_ADD`        | Adds or modifies an event                   |
+   +---------------------------+---------------------------------------------+
+   | :const:`KQ_EV_DELETE`     | Removes an event from the queue             |
+   +---------------------------+---------------------------------------------+
+   | :const:`KQ_EV_ENABLE`     | Permitscontrol() to returns the event       |
+   +---------------------------+---------------------------------------------+
+   | :const:`KQ_EV_DISABLE`    | Disablesevent                               |
+   +---------------------------+---------------------------------------------+
+   | :const:`KQ_EV_ONESHOT`    | Removes event after first occurence         |
+   +---------------------------+---------------------------------------------+
+   | :const:`KQ_EV_CLEAR`      | Reset the state after an event is retrieved |
+   +---------------------------+---------------------------------------------+
+   | :const:`KQ_EV_SYSFLAGS`   | internal event                              |
+   +---------------------------+---------------------------------------------+
+   | :const:`KQ_EV_FLAG1`      | internal event                              |
+   +---------------------------+---------------------------------------------+
+   | :const:`KQ_EV_EOF`        | Filter specific EOF condition               |
+   +---------------------------+---------------------------------------------+
+   | :const:`KQ_EV_ERROR`      | See return values                           |
+   +---------------------------+---------------------------------------------+
+
+
+   .. attribute:: fflags
+
+   Filter specific flags
+
+
+   *:const:`KQ_FILTER_READ` and  :const:`KQ_FILTER_WRITE` filter flags
+
+   +----------------------------+--------------------------------------------+
+   | Constant                   | Meaning                                    |
+   +============================+============================================+
+   | :const:`KQ_NOTE_LOWAT`     | low water mark of a socket buffer          |
+   +----------------------------+--------------------------------------------+
+
+
+   *:const:`KQ_FILTER_VNODE` filter flags*
+
+   +----------------------------+--------------------------------------------+
+   | Constant                   | Meaning                                    |
+   +============================+============================================+
+   | :const:`KQ_NOTE_DELETE`    | *unlink()* was called                      |
+   +----------------------------+--------------------------------------------+
+   | :const:`KQ_NOTE_WRITE`     | a write occured                            |
+   +----------------------------+--------------------------------------------+
+   | :const:`KQ_NOTE_EXTEND`    | the file was extended                      |
+   +----------------------------+--------------------------------------------+
+   | :const:`KQ_NOTE_ATTRIB`    | an attribute was changed                   |
+   +----------------------------+--------------------------------------------+
+   | :const:`KQ_NOTE_LINK`      | the link count has changed                 |
+   +----------------------------+--------------------------------------------+
+   | :const:`KQ_NOTE_RENAME`    | the file was renamed                       |
+   +----------------------------+--------------------------------------------+
+   | :const:`KQ_NOTE_REVOKE`    | access to the file was revoked             |
+   +----------------------------+--------------------------------------------+
+
+
+   *:const:`KQ_FILTER_PROC` filter flags*
+
+   +----------------------------+--------------------------------------------+
+   | Constant                   | Meaning                                    |
+   +============================+============================================+
+   | :const:`KQ_NOTE_EXIT`      | the process has exited                     |
+   +----------------------------+--------------------------------------------+
+   | :const:`KQ_NOTE_FORK`      | the process has called *fork()*            |
+   +----------------------------+--------------------------------------------+
+   | :const:`KQ_NOTE_EXEC`      | the process has executed a new process     |
+   +----------------------------+--------------------------------------------+
+   | :const:`KQ_NOTE_PCTRLMASK` | internal filter flag                       |
+   +----------------------------+--------------------------------------------+
+   | :const:`KQ_NOTE_PDATAMASK` | internal filter flag                       |
+   +----------------------------+--------------------------------------------+
+   | :const:`KQ_NOTE_TRACK`     | follow a process across *fork()*           |
+   +----------------------------+--------------------------------------------+
+   | :const:`KQ_NOTE_CHILD`     | returned on the child process for          |
+   |                            | *NOTE_TRACK*                               |
+   +----------------------------+--------------------------------------------+
+   | :const:`KQ_NOTE_TRACKERR`  | unable to attach to a child                |
+   +----------------------------+--------------------------------------------+
+
+   *:const:`KQ_FILTER_NETDEV` filter flags* [not available on Mac OS X]
+
+   +----------------------------+--------------------------------------------+
+   | Constant                   | Meaning                                    |
+   +============================+============================================+
+   | :const:`KQ_NOTE_LINKUP`    | link is up                                 |
+   +----------------------------+--------------------------------------------+
+   | :const:`KQ_NOTE_LINKDOWN`  | link is down                               |
+   +----------------------------+--------------------------------------------+
+   | :const:`KQ_NOTE_LINKINV`   | link state is invalid                      |
+   +----------------------------+--------------------------------------------+
+
+
+   .. attribute:: data
+
+   Filter specific data
+
+
+   .. attribute:: udata
+
+   User defined value

Modified: python/trunk/Doc/license.rst
==============================================================================
--- python/trunk/Doc/license.rst	(original)
+++ python/trunk/Doc/license.rst	Sat Mar 22 00:49:44 2008
@@ -645,3 +645,58 @@
    ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
    OF THIS SOFTWARE.
 
+
+test_epoll
+----------
+
+The :mod:`test_epoll` contains the following notice::
+
+  Copyright (c) 2001-2006 Twisted Matrix Laboratories.
+
+  Permission is hereby granted, free of charge, to any person obtaining
+  a copy of this software and associated documentation files (the
+  "Software"), to deal in the Software without restriction, including
+  without limitation the rights to use, copy, modify, merge, publish,
+  distribute, sublicense, and/or sell copies of the Software, and to
+  permit persons to whom the Software is furnished to do so, subject to
+  the following conditions:
+
+  The above copyright notice and this permission notice shall be
+  included in all copies or substantial portions of the Software.
+
+  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+  EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+  MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+  NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+  LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+  OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+  WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+Select kqueue
+-------------
+
+The :mod:`select` and contains the following notice for the kqueue interface::
+
+  Copyright (c) 2000 Doug White, 2006 James Knight, 2007 Christian Heimes
+  All rights reserved.
+
+  Redistribution and use in source and binary forms, with or without
+  modification, are permitted provided that the following conditions
+  are met:
+  1. Redistributions of source code must retain the above copyright
+     notice, this list of conditions and the following disclaimer.
+  2. Redistributions in binary form must reproduce the above copyright
+     notice, this list of conditions and the following disclaimer in the
+     documentation and/or other materials provided with the distribution.
+
+  THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+  ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+  ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+  FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+  DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+  OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+  HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+  LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+  OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+  SUCH DAMAGE.

Modified: python/trunk/Lib/test/regrtest.py
==============================================================================
--- python/trunk/Lib/test/regrtest.py	(original)
+++ python/trunk/Lib/test/regrtest.py	Sat Mar 22 00:49:44 2008
@@ -825,10 +825,12 @@
         test_dl
         test_fcntl
         test_fork1
+        test_epoll
         test_gdbm
         test_grp
         test_ioctl
         test_largefile
+        test_kqueue
         test_mhlib
         test_openpty
         test_ossaudiodev
@@ -850,6 +852,7 @@
         test_curses
         test_dl
         test_largefile
+        test_kqueue
         test_ossaudiodev
         """,
    'mac':
@@ -866,10 +869,12 @@
         test_dl
         test_fcntl
         test_fork1
+        test_epoll
         test_grp
         test_ioctl
         test_largefile
         test_locale
+        test_kqueue
         test_mmap
         test_openpty
         test_ossaudiodev
@@ -890,7 +895,9 @@
         test_bsddb
         test_bsddb185
         test_dl
+        test_epoll
         test_largefile
+        test_kqueue
         test_minidom
         test_openpty
         test_pyexpat
@@ -902,7 +909,9 @@
         test_bsddb
         test_bsddb185
         test_dl
+        test_epoll
         test_largefile
+        test_kqueue
         test_minidom
         test_openpty
         test_pyexpat
@@ -916,9 +925,11 @@
         test_bsddb185
         test_dl
         test_fork1
+        test_epoll
         test_gettext
         test_largefile
         test_locale
+        test_kqueue
         test_minidom
         test_openpty
         test_pyexpat
@@ -943,10 +954,12 @@
         test_dl
         test_fcntl
         test_fork1
+        test_epoll
         test_gdbm
         test_grp
         test_largefile
         test_locale
+        test_kqueue
         test_mmap
         test_openpty
         test_poll
@@ -967,9 +980,11 @@
         test_bsddb
         test_bsddb3
         test_curses
+        test_epoll
         test_gdbm
         test_largefile
         test_locale
+        test_kqueue
         test_minidom
         test_ossaudiodev
         test_poll
@@ -980,6 +995,8 @@
         test_bsddb185
         test_curses
         test_dbm
+        test_epoll
+        test_kqueue
         test_gdbm
         test_gzip
         test_openpty
@@ -992,10 +1009,12 @@
         test_bsddb185
         test_curses
         test_dl
+        test_epoll
         test_gdbm
         test_gzip
         test_largefile
         test_locale
+        test_kqueue
         test_minidom
         test_openpty
         test_pyexpat
@@ -1009,8 +1028,10 @@
         test_curses
         test_dl
         test_gdbm
+        test_epoll
         test_largefile
         test_locale
+        test_kqueue
         test_mhlib
         test_mmap
         test_poll
@@ -1023,7 +1044,9 @@
         test_bsddb3
         test_curses
         test_dbm
+        test_epoll
         test_ioctl
+        test_kqueue
         test_largefile
         test_locale
         test_ossaudiodev
@@ -1037,6 +1060,8 @@
         test_commands
         test_curses
         test_dl
+        test_epoll
+        test_kqueue
         test_largefile
         test_mhlib
         test_mmap
@@ -1050,6 +1075,7 @@
         """
         test_bsddb
         test_bsddb3
+        test_epoll
         test_gdbm
         test_locale
         test_ossaudiodev
@@ -1068,8 +1094,10 @@
         test_bsddb3
         test_bz2
         test_dl
+        test_epoll
         test_gdbm
         test_gzip
+        test_kqueue
         test_ossaudiodev
         test_tcl
         test_zipimport
@@ -1081,6 +1109,7 @@
         test_bsddb3
         test_ctypes
         test_dl
+        test_epoll
         test_gdbm
         test_locale
         test_normalization
@@ -1096,6 +1125,7 @@
         test_ctypes
         test_curses
         test_dl
+        test_epoll
         test_gdbm
         test_locale
         test_ossaudiodev

Added: python/trunk/Lib/test/test_epoll.py
==============================================================================
--- (empty file)
+++ python/trunk/Lib/test/test_epoll.py	Sat Mar 22 00:49:44 2008
@@ -0,0 +1,189 @@
+# Copyright (c) 2001-2006 Twisted Matrix Laboratories.
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be
+# included in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+"""
+Tests for epoll wrapper.
+"""
+import os
+import socket
+import errno
+import time
+import select
+import tempfile
+import unittest
+
+from test import test_support
+if not hasattr(select, "epoll"):
+    raise test_support.TestSkipped("test works only on Linux 2.6")
+
+class TestEPoll(unittest.TestCase):
+
+    def setUp(self):
+        self.serverSocket = socket.socket()
+        self.serverSocket.bind(('127.0.0.1', 0))
+        self.serverSocket.listen(1)
+        self.connections = [self.serverSocket]
+
+
+    def tearDown(self):
+        for skt in self.connections:
+            skt.close()
+
+    def _connected_pair(self):
+        client = socket.socket()
+        client.setblocking(False)
+        try:
+            client.connect(('127.0.0.1', self.serverSocket.getsockname()[1]))
+        except socket.error, e:
+            self.assertEquals(e.args[0], errno.EINPROGRESS)
+        else:
+            raise AssertionError("Connect should have raised EINPROGRESS")
+        server, addr = self.serverSocket.accept()
+
+        self.connections.extend((client, server))
+        return client, server
+
+    def test_create(self):
+        try:
+            ep = select.epoll(16)
+        except OSError, e:
+            raise AssertionError(str(e))
+        self.assert_(ep.fileno() > 0, ep.fileno())
+        self.assert_(not ep.closed)
+        ep.close()
+        self.assert_(ep.closed)
+        self.assertRaises(ValueError, ep.fileno)
+
+    def test_badcreate(self):
+        self.assertRaises(TypeError, select.epoll, 1, 2, 3)
+        self.assertRaises(TypeError, select.epoll, 'foo')
+        self.assertRaises(TypeError, select.epoll, None)
+        self.assertRaises(TypeError, select.epoll, ())
+        self.assertRaises(TypeError, select.epoll, ['foo'])
+        self.assertRaises(TypeError, select.epoll, {})
+
+    def test_add(self):
+        server, client = self._connected_pair()
+
+        ep = select.epoll(2)
+        try:
+            ep.register(server.fileno(), select.EPOLLIN | select.EPOLLOUT)
+            ep.register(client.fileno(), select.EPOLLIN | select.EPOLLOUT)
+        finally:
+            ep.close()
+
+    def test_fromfd(self):
+        server, client = self._connected_pair()
+
+        ep = select.epoll(2)
+        ep2 = select.epoll.fromfd(ep.fileno())
+
+        ep2.register(server.fileno(), select.EPOLLIN | select.EPOLLOUT)
+        ep2.register(client.fileno(), select.EPOLLIN | select.EPOLLOUT)
+
+        events = ep.poll(1, 4)
+        events2 = ep2.poll(0.9, 4)
+        self.assertEqual(len(events), 2)
+        self.assertEqual(len(events2), 2)
+
+        ep.close()
+        try:
+            ep2.poll(1, 4)
+        except IOError, e:
+            self.failUnlessEqual(e.args[0], errno.EBADF, e)
+        else:
+            self.fail("epoll on closed fd didn't raise EBADF")
+
+    def test_control_and_wait(self):
+        client, server = self._connected_pair()
+
+        ep = select.epoll(16)
+        ep.register(server.fileno(),
+                   select.EPOLLIN | select.EPOLLOUT | select.EPOLLET)
+        ep.register(client.fileno(),
+                   select.EPOLLIN | select.EPOLLOUT | select.EPOLLET)
+
+        now = time.time()
+        events = ep.poll(1, 4)
+        then = time.time()
+        self.failIf(then - now > 0.1, then - now)
+
+        events.sort()
+        expected = [(client.fileno(), select.EPOLLOUT),
+                    (server.fileno(), select.EPOLLOUT)]
+        expected.sort()
+
+        self.assertEquals(events, expected)
+        self.failIf(then - now > 0.01, then - now)
+
+        now = time.time()
+        events = ep.poll(timeout=2.1, maxevents=4)
+        then = time.time()
+        self.failIf(events)
+
+        client.send("Hello!")
+        server.send("world!!!")
+
+        now = time.time()
+        events = ep.poll(1, 4)
+        then = time.time()
+        self.failIf(then - now > 0.01)
+
+        events.sort()
+        expected = [(client.fileno(), select.EPOLLIN | select.EPOLLOUT),
+                    (server.fileno(), select.EPOLLIN | select.EPOLLOUT)]
+        expected.sort()
+
+        self.assertEquals(events, expected)
+
+        ep.unregister(client.fileno())
+        ep.modify(server.fileno(), select.EPOLLOUT)
+        now = time.time()
+        events = ep.poll(1, 4)
+        then = time.time()
+        self.failIf(then - now > 0.01)
+
+        expected = [(server.fileno(), select.EPOLLOUT)]
+        self.assertEquals(events, expected)
+
+    def test_errors(self):
+        self.assertRaises(ValueError, select.epoll, -2)
+        self.assertRaises(ValueError, select.epoll().register, -1,
+                          select.EPOLLIN)
+
+    def test_unregister_closed(self):
+        server, client = self._connected_pair()
+        fd = server.fileno()
+        ep = select.epoll(16)
+        ep.register(server)
+
+        now = time.time()
+        events = ep.poll(1, 4)
+        then = time.time()
+        self.failIf(then - now > 0.01)
+
+        server.close()
+        ep.unregister(fd)
+
+def test_main():
+    test_support.run_unittest(TestEPoll)
+
+if __name__ == "__main__":
+    test_main()

Added: python/trunk/Lib/test/test_kqueue.py
==============================================================================
--- (empty file)
+++ python/trunk/Lib/test/test_kqueue.py	Sat Mar 22 00:49:44 2008
@@ -0,0 +1,166 @@
+"""
+Tests for kqueue wrapper.
+"""
+import socket
+import errno
+import time
+import select
+import sys
+import unittest
+
+from test import test_support
+if not hasattr(select, "kqueue"):
+    raise test_support.TestSkipped("test works only on BSD")
+
+class TestKQueue(unittest.TestCase):
+    def test_create_queue(self):
+        kq = select.kqueue()
+        self.assert_(kq.fileno() > 0, kq.fileno())
+        self.assert_(not kq.closed)
+        kq.close()
+        self.assert_(kq.closed)
+        self.assertRaises(ValueError, kq.fileno)
+
+    def test_create_event(self):
+        fd = sys.stderr.fileno()
+        ev = select.kevent(fd)
+        other = select.kevent(1000)
+        self.assertEqual(ev.ident, fd)
+        self.assertEqual(ev.filter, select.KQ_FILTER_READ)
+        self.assertEqual(ev.flags, select.KQ_EV_ADD)
+        self.assertEqual(ev.fflags, 0)
+        self.assertEqual(ev.data, 0)
+        self.assertEqual(ev.udata, 0)
+        self.assertEqual(ev, ev)
+        self.assertNotEqual(ev, other)
+        self.assertEqual(cmp(ev, other), -1)
+        self.assert_(ev < other)
+        self.assert_(other >= ev)
+        self.assertRaises(TypeError, cmp, ev, None)
+        self.assertRaises(TypeError, cmp, ev, 1)
+        self.assertRaises(TypeError, cmp, ev, "ev")
+
+        ev = select.kevent(fd, select.KQ_FILTER_WRITE)
+        self.assertEqual(ev.ident, fd)
+        self.assertEqual(ev.filter, select.KQ_FILTER_WRITE)
+        self.assertEqual(ev.flags, select.KQ_EV_ADD)
+        self.assertEqual(ev.fflags, 0)
+        self.assertEqual(ev.data, 0)
+        self.assertEqual(ev.udata, 0)
+        self.assertEqual(ev, ev)
+        self.assertNotEqual(ev, other)
+
+        ev = select.kevent(fd, select.KQ_FILTER_WRITE, select.KQ_EV_ONESHOT)
+        self.assertEqual(ev.ident, fd)
+        self.assertEqual(ev.filter, select.KQ_FILTER_WRITE)
+        self.assertEqual(ev.flags, select.KQ_EV_ONESHOT)
+        self.assertEqual(ev.fflags, 0)
+        self.assertEqual(ev.data, 0)
+        self.assertEqual(ev.udata, 0)
+        self.assertEqual(ev, ev)
+        self.assertNotEqual(ev, other)
+
+        ev = select.kevent(1, 2, 3, 4, 5, 6)
+        self.assertEqual(ev.ident, 1)
+        self.assertEqual(ev.filter, 2)
+        self.assertEqual(ev.flags, 3)
+        self.assertEqual(ev.fflags, 4)
+        self.assertEqual(ev.data, 5)
+        self.assertEqual(ev.udata, 6)
+        self.assertEqual(ev, ev)
+        self.assertNotEqual(ev, other)
+
+    def test_queue_event(self):
+        serverSocket = socket.socket()
+        serverSocket.bind(('127.0.0.1', 0))
+        serverSocket.listen(1)
+        client = socket.socket()
+        client.setblocking(False)
+        try:
+            client.connect(('127.0.0.1', serverSocket.getsockname()[1]))
+        except socket.error, e:
+            self.assertEquals(e.args[0], errno.EINPROGRESS)
+        else:
+            #raise AssertionError("Connect should have raised EINPROGRESS")
+            pass # FreeBSD doesn't raise an exception here
+        server, addr = serverSocket.accept()
+
+        if sys.platform.startswith("darwin"):
+            flags = select.KQ_EV_ADD | select.KQ_EV_ENABLE
+        else:
+            flags = 0
+
+        kq = select.kqueue()
+        kq2 = select.kqueue.fromfd(kq.fileno())
+
+        ev = select.kevent(server.fileno(),
+                           select.KQ_FILTER_WRITE,
+                           select.KQ_EV_ADD | select.KQ_EV_ENABLE)
+        kq.control([ev], 0)
+        ev = select.kevent(server.fileno(),
+                           select.KQ_FILTER_READ,
+                           select.KQ_EV_ADD | select.KQ_EV_ENABLE)
+        kq.control([ev], 0)
+        ev = select.kevent(client.fileno(),
+                           select.KQ_FILTER_WRITE,
+                           select.KQ_EV_ADD | select.KQ_EV_ENABLE)
+        kq2.control([ev], 0)
+        ev = select.kevent(client.fileno(),
+                           select.KQ_FILTER_READ,
+                           select.KQ_EV_ADD | select.KQ_EV_ENABLE)
+        kq2.control([ev], 0)
+
+        events = kq.control(None, 4, 1)
+        events = [(e.ident, e.filter, e.flags) for e in events]
+        events.sort()
+        self.assertEquals(events, [
+            (client.fileno(), select.KQ_FILTER_WRITE, flags),
+            (server.fileno(), select.KQ_FILTER_WRITE, flags)])
+
+        client.send("Hello!")
+        server.send("world!!!")
+
+        events = kq.control(None, 4, 1)
+        # We may need to call it several times
+        for i in range(5):
+            if len(events) == 4:
+                break
+            events = kq.control(None, 4, 1)
+        events = [(e.ident, e.filter, e.flags) for e in events]
+        events.sort()
+
+        self.assertEquals(events, [
+            (client.fileno(), select.KQ_FILTER_WRITE, flags),
+            (client.fileno(), select.KQ_FILTER_READ, flags),
+            (server.fileno(), select.KQ_FILTER_WRITE, flags),
+            (server.fileno(), select.KQ_FILTER_READ, flags)])
+
+        # Remove completely client, and server read part
+        ev = select.kevent(client.fileno(),
+                           select.KQ_FILTER_WRITE,
+                           select.KQ_EV_DELETE)
+        kq.control([ev], 0)
+        ev = select.kevent(client.fileno(),
+                           select.KQ_FILTER_READ,
+                           select.KQ_EV_DELETE)
+        kq.control([ev], 0)
+        ev = select.kevent(server.fileno(),
+                           select.KQ_FILTER_READ,
+                           select.KQ_EV_DELETE)
+        kq.control([ev], 0, 0)
+
+        events = kq.control([], 4, 0.99)
+        events = [(e.ident, e.filter, e.flags) for e in events]
+        events.sort()
+        self.assertEquals(events, [
+            (server.fileno(), select.KQ_FILTER_WRITE, flags)])
+
+        client.close()
+        server.close()
+        serverSocket.close()
+
+def test_main():
+    test_support.run_unittest(TestKQueue)
+
+if __name__ == "__main__":
+    test_main()

Modified: python/trunk/Lib/test/test_poll.py
==============================================================================
--- python/trunk/Lib/test/test_poll.py	(original)
+++ python/trunk/Lib/test/test_poll.py	Sat Mar 22 00:49:44 2008
@@ -34,7 +34,8 @@
 
         for i in range(NUM_PIPES):
             rd, wr = os.pipe()
-            p.register(rd, select.POLLIN)
+            p.register(rd)
+            p.modify(rd, select.POLLIN)
             p.register(wr, select.POLLOUT)
             readers.append(rd)
             writers.append(wr)

Modified: python/trunk/Misc/NEWS
==============================================================================
--- python/trunk/Misc/NEWS	(original)
+++ python/trunk/Misc/NEWS	Sat Mar 22 00:49:44 2008
@@ -1363,6 +1363,8 @@
 Extension Modules
 -----------------
 
+- Patch #1657: added select.epoll and select.kqueue
+
 - Patch #1506171: added operator.methodcaller().
 
 - Patch #1826: operator.attrgetter() now supports dotted attribute paths.

Modified: python/trunk/Modules/selectmodule.c
==============================================================================
--- python/trunk/Modules/selectmodule.c	(original)
+++ python/trunk/Modules/selectmodule.c	Sat Mar 22 00:49:44 2008
@@ -7,6 +7,7 @@
 */
 
 #include "Python.h"
+#include <structmember.h>
 
 #ifdef __APPLE__
     /* Perform runtime testing for a broken poll on OSX to make it easier
@@ -56,7 +57,6 @@
 #  endif
 #endif
 
-
 static PyObject *SelectError;
 
 /* list of Python objects and their file descriptor */
@@ -241,7 +241,7 @@
 		seconds = (long)timeout;
 		timeout = timeout - (double)seconds;
 		tv.tv_sec = seconds;
-		tv.tv_usec = (long)(timeout*1000000.0);
+		tv.tv_usec = (long)(timeout * 1E6);
 		tvp = &tv;
 	}
 
@@ -405,11 +405,59 @@
 		return NULL;
 
 	self->ufd_uptodate = 0;
-		       
+
 	Py_INCREF(Py_None);
 	return Py_None;
 }
 
+PyDoc_STRVAR(poll_modify_doc,
+"modify(fd, eventmask) -> None\n\n\
+Modify an already register file descriptor.\n\
+fd -- either an integer, or an object with a fileno() method returning an\n\
+      int.\n\
+events -- an optional bitmask describing the type of events to check for");
+
+static PyObject *
+poll_modify(pollObject *self, PyObject *args)
+{
+	PyObject *o, *key, *value;
+	int fd, events;
+	int err;
+
+	if (!PyArg_ParseTuple(args, "Oi:modify", &o, &events)) {
+		return NULL;
+	}
+  
+	fd = PyObject_AsFileDescriptor(o);
+	if (fd == -1) return NULL;
+
+	/* Modify registered fd */
+	key = PyInt_FromLong(fd);
+	if (key == NULL)
+		return NULL;
+	if (PyDict_GetItem(self->dict, key) == NULL) {
+		errno = ENOENT;
+		PyErr_SetFromErrno(PyExc_IOError);
+		return NULL;
+	}
+	value = PyInt_FromLong(events);
+	if (value == NULL) {
+		Py_DECREF(key);
+		return NULL;
+	}
+	err = PyDict_SetItem(self->dict, key, value);
+	Py_DECREF(key);
+	Py_DECREF(value);
+	if (err < 0)
+		return NULL;
+
+	self->ufd_uptodate = 0;
+
+	Py_INCREF(Py_None);
+	return Py_None;
+}
+
+
 PyDoc_STRVAR(poll_unregister_doc,
 "unregister(fd) -> None\n\n\
 Remove a file descriptor being tracked by the polling object.");
@@ -483,9 +531,9 @@
 			return NULL;
 
 	/* call poll() */
-	Py_BEGIN_ALLOW_THREADS;
+	Py_BEGIN_ALLOW_THREADS
 	poll_result = poll(self->ufds, self->ufd_len, timeout);
-	Py_END_ALLOW_THREADS;
+	Py_END_ALLOW_THREADS
  
 	if (poll_result < 0) {
 		PyErr_SetFromErrno(SelectError);
@@ -543,7 +591,9 @@
 static PyMethodDef poll_methods[] = {
 	{"register",	(PyCFunction)poll_register,	
 	 METH_VARARGS,  poll_register_doc},
-	{"unregister",	(PyCFunction)poll_unregister,	
+	{"modify",	(PyCFunction)poll_modify,
+	 METH_VARARGS,  poll_modify_doc},
+	{"unregister",	(PyCFunction)poll_unregister,
 	 METH_O,        poll_unregister_doc},
 	{"poll",	(PyCFunction)poll_poll,	
 	 METH_VARARGS,  poll_poll_doc},
@@ -646,6 +696,1007 @@
 
 #endif /* HAVE_POLL */
 
+#ifdef HAVE_EPOLL
+/* **************************************************************************
+ *                      epoll interface for Linux 2.6
+ *
+ * Written by Christian Heimes
+ * Inspired by Twisted's _epoll.pyx and select.poll()
+ */
+
+#ifdef HAVE_SYS_EPOLL_H
+#include <sys/epoll.h>
+#endif
+
+typedef struct {
+	PyObject_HEAD
+	SOCKET epfd;			/* epoll control file descriptor */
+} pyEpoll_Object;
+
+static PyTypeObject pyEpoll_Type;
+#define pyepoll_CHECK(op) (PyObject_TypeCheck((op), &pyEpoll_Type))
+
+static PyObject *
+pyepoll_err_closed(void)
+{
+	PyErr_SetString(PyExc_ValueError, "I/O operation on closed epoll fd");
+	return NULL;
+}
+
+static int
+pyepoll_internal_close(pyEpoll_Object *self)
+{
+	int save_errno = 0;
+	if (self->epfd >= 0) {
+		int epfd = self->epfd;
+		self->epfd = -1;
+		Py_BEGIN_ALLOW_THREADS
+		if (close(epfd) < 0)
+			save_errno = errno;
+		Py_END_ALLOW_THREADS
+	}
+	return save_errno;
+}
+
+static PyObject *
+newPyEpoll_Object(PyTypeObject *type, int sizehint, SOCKET fd)
+{
+	pyEpoll_Object *self;
+	
+	if (sizehint == -1) {
+		sizehint = FD_SETSIZE-1;
+	}
+	else if (sizehint < 1) {
+		PyErr_Format(PyExc_ValueError,
+			     "sizehint must be greater zero, got %d",
+			     sizehint);
+		return NULL;
+	}
+
+	assert(type != NULL && type->tp_alloc != NULL);
+	self = (pyEpoll_Object *) type->tp_alloc(type, 0);
+	if (self == NULL)
+		return NULL;
+
+	if (fd == -1) {
+		Py_BEGIN_ALLOW_THREADS
+		self->epfd = epoll_create(sizehint);
+		Py_END_ALLOW_THREADS
+	}
+	else {
+		self->epfd = fd;
+	}
+	if (self->epfd < 0) {
+		Py_DECREF(self);
+		PyErr_SetFromErrno(PyExc_IOError);
+		return NULL;
+	}
+	return (PyObject *)self;
+}
+
+
+static PyObject *
+pyepoll_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
+{
+	int sizehint = -1;
+	static char *kwlist[] = {"sizehint", NULL};
+
+	if (!PyArg_ParseTupleAndKeywords(args, kwds, "|i:epoll", kwlist,
+					 &sizehint))
+		return NULL;
+
+	return newPyEpoll_Object(type, sizehint, -1);
+}
+
+
+static void
+pyepoll_dealloc(pyEpoll_Object *self)
+{
+	(void)pyepoll_internal_close(self);
+	Py_TYPE(self)->tp_free(self);
+}
+
+static PyObject*
+pyepoll_close(pyEpoll_Object *self)
+{
+	errno = pyepoll_internal_close(self);
+	if (errno < 0) {
+		PyErr_SetFromErrno(PyExc_IOError);
+		return NULL;
+	}
+	Py_RETURN_NONE;
+}
+
+PyDoc_STRVAR(pyepoll_close_doc,
+"close() -> None\n\
+\n\
+Close the epoll control file descriptor. Further operations on the epoll\n\
+object will raise an exception.");
+
+static PyObject*
+pyepoll_get_closed(pyEpoll_Object *self)
+{
+	if (self->epfd < 0)
+		Py_RETURN_TRUE;
+	else
+		Py_RETURN_FALSE;
+}
+
+static PyObject*
+pyepoll_fileno(pyEpoll_Object *self)
+{
+	if (self->epfd < 0)
+		return pyepoll_err_closed();
+	return PyInt_FromLong(self->epfd);
+}
+
+PyDoc_STRVAR(pyepoll_fileno_doc,
+"fileno() -> int\n\
+\n\
+Return the epoll control file descriptor.");
+
+static PyObject*
+pyepoll_fromfd(PyObject *cls, PyObject *args)
+{
+	SOCKET fd;
+
+	if (!PyArg_ParseTuple(args, "i:fromfd", &fd))
+		return NULL;
+
+	return newPyEpoll_Object((PyTypeObject*)cls, -1, fd);
+}
+
+PyDoc_STRVAR(pyepoll_fromfd_doc,
+"fromfd(fd) -> epoll\n\
+\n\
+Create an epoll object from a given control fd.");
+
+static PyObject *
+pyepoll_internal_ctl(int epfd, int op, PyObject *pfd, unsigned int events)
+{
+	struct epoll_event ev;
+	int result;
+	int fd;
+
+	if (epfd < 0)
+		return pyepoll_err_closed();
+
+	fd = PyObject_AsFileDescriptor(pfd);
+	if (fd == -1) {
+		return NULL;
+	}
+
+	switch(op) {
+	    case EPOLL_CTL_ADD:
+	    case EPOLL_CTL_MOD:
+		ev.events = events;
+		ev.data.fd = fd;
+		Py_BEGIN_ALLOW_THREADS
+		result = epoll_ctl(epfd, op, fd, &ev);
+		Py_END_ALLOW_THREADS
+		break;
+	    case EPOLL_CTL_DEL:
+		/* In kernel versions before 2.6.9, the EPOLL_CTL_DEL
+		 * operation required a non-NULL pointer in event, even
+		 * though this argument is ignored. */
+		Py_BEGIN_ALLOW_THREADS
+		result = epoll_ctl(epfd, op, fd, &ev);
+		if (errno == EBADF) {
+			/* fd already closed */
+			result = 0;
+			errno = 0;
+		}
+		Py_END_ALLOW_THREADS
+		break;
+	    default:
+		result = -1;
+		errno = EINVAL;
+	}
+
+	if (result < 0) {
+		PyErr_SetFromErrno(PyExc_IOError);
+		return NULL;
+	}
+	Py_RETURN_NONE;
+}
+
+static PyObject *
+pyepoll_register(pyEpoll_Object *self, PyObject *args, PyObject *kwds)
+{
+	PyObject *pfd;
+	unsigned int events = EPOLLIN | EPOLLOUT | EPOLLPRI;
+	static char *kwlist[] = {"fd", "eventmask", NULL};
+
+	if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|I:register", kwlist,
+					 &pfd, &events)) {
+		return NULL;
+	}
+
+	return pyepoll_internal_ctl(self->epfd, EPOLL_CTL_ADD, pfd, events);
+}
+
+PyDoc_STRVAR(pyepoll_register_doc,
+"register(fd[, eventmask]) -> bool\n\
+\n\
+Registers a new fd or modifies an already registered fd. register returns\n\
+True if a new fd was registered or False if the event mask for fd was modified.\n\
+fd is the target file descriptor of the operation\n\
+events is a bit set composed of the various EPOLL constants, the default\n\
+is EPOLL_IN | EPOLL_OUT | EPOLL_PRI.\n\
+\n\
+The epoll interface supports all file descriptors that support poll.");
+
+static PyObject *
+pyepoll_modify(pyEpoll_Object *self, PyObject *args, PyObject *kwds)
+{
+	PyObject *pfd;
+	unsigned int events;
+	static char *kwlist[] = {"fd", "eventmask", NULL};
+
+	if (!PyArg_ParseTupleAndKeywords(args, kwds, "OI:modify", kwlist,
+					 &pfd, &events)) {
+		return NULL;
+	}
+
+	return pyepoll_internal_ctl(self->epfd, EPOLL_CTL_MOD, pfd, events);
+}
+
+PyDoc_STRVAR(pyepoll_modify_doc,
+"modify(fd, eventmask) -> None\n\
+\n\
+fd is the target file descriptor of the operation\n\
+events is a bit set composed of the various EPOLL constants");
+
+static PyObject *
+pyepoll_unregister(pyEpoll_Object *self, PyObject *args, PyObject *kwds)
+{
+	PyObject *pfd;
+	static char *kwlist[] = {"fd", NULL};
+
+	if (!PyArg_ParseTupleAndKeywords(args, kwds, "O:unregister", kwlist,
+					 &pfd)) {
+		return NULL;
+	}
+
+	return pyepoll_internal_ctl(self->epfd, EPOLL_CTL_DEL, pfd, 0);
+}
+
+PyDoc_STRVAR(pyepoll_unregister_doc,
+"unregister(fd) -> None\n\
+\n\
+fd is the target file descriptor of the operation.");
+
+static PyObject *
+pyepoll_poll(pyEpoll_Object *self, PyObject *args, PyObject *kwds)
+{
+	double dtimeout = -1.;
+	int timeout;
+	int maxevents = -1;
+	int nfds, i;
+	PyObject *elist = NULL, *etuple = NULL;
+	struct epoll_event *evs = NULL;
+	static char *kwlist[] = {"timeout", "maxevents", NULL};
+
+	if (self->epfd < 0)
+		return pyepoll_err_closed();
+
+	if (!PyArg_ParseTupleAndKeywords(args, kwds, "|di:poll", kwlist,
+					 &dtimeout, &maxevents)) {
+		return NULL;
+	}
+
+	if (dtimeout < 0) {
+		timeout = -1;
+	}
+	else if (dtimeout * 1000.0 > INT_MAX) {
+		PyErr_SetString(PyExc_OverflowError,
+				"timeout is too large");
+	}
+	else {
+		timeout = (int)(dtimeout * 1000.0);
+	}
+
+	if (maxevents == -1) {
+		maxevents = FD_SETSIZE-1;
+	}
+	else if (maxevents < 1) {
+		PyErr_Format(PyExc_ValueError,
+			     "maxevents must be greater than 0, got %d",
+			     maxevents);
+		return NULL;
+	}
+
+	evs = PyMem_New(struct epoll_event, maxevents);
+	if (evs == NULL) {
+		Py_DECREF(self);
+		PyErr_NoMemory();
+		return NULL;
+	}
+
+	Py_BEGIN_ALLOW_THREADS
+	nfds = epoll_wait(self->epfd, evs, maxevents, timeout);
+	Py_END_ALLOW_THREADS
+	if (nfds < 0) {
+		PyErr_SetFromErrno(PyExc_IOError);
+		goto error;
+	}
+
+	elist = PyList_New(nfds);
+	if (elist == NULL) {
+		goto error;
+	}
+
+	for (i = 0; i < nfds; i++) {
+		etuple = Py_BuildValue("iI", evs[i].data.fd,
+				       evs[i].events);
+		if (etuple == NULL) {
+			goto error;
+		}
+		PyList_SET_ITEM(elist, i, etuple);
+	}
+
+	if (0) {
+	    error:
+		Py_CLEAR(elist);
+		Py_XDECREF(etuple);
+	}
+	PyMem_Free(evs);
+	return elist;
+}
+
+PyDoc_STRVAR(pyepoll_poll_doc,
+"poll([timeout=-1[, maxevents=-1]]) -> [(fd, events), (...)]\n\
+\n\
+Wait for events on the epoll file descriptor for a maximum time of timeout\n\
+in seconds (as float). -1 makes poll wait indefinitely.\n\
+Up to maxevents are returned to the caller.");
+
+static PyMethodDef pyepoll_methods[] = {
+	{"fromfd",	(PyCFunction)pyepoll_fromfd,
+	 METH_VARARGS | METH_CLASS, pyepoll_fromfd_doc},
+	{"close",	(PyCFunction)pyepoll_close,	METH_NOARGS,
+	 pyepoll_close_doc},
+	{"fileno",	(PyCFunction)pyepoll_fileno,	METH_NOARGS,
+	 pyepoll_fileno_doc},
+	{"modify",	(PyCFunction)pyepoll_modify,
+	 METH_VARARGS | METH_KEYWORDS,	pyepoll_modify_doc},
+	{"register",	(PyCFunction)pyepoll_register,
+	 METH_VARARGS | METH_KEYWORDS,	pyepoll_register_doc},
+	{"unregister",	(PyCFunction)pyepoll_unregister,
+	 METH_VARARGS | METH_KEYWORDS,	pyepoll_unregister_doc},
+	{"poll",	(PyCFunction)pyepoll_poll,
+	 METH_VARARGS | METH_KEYWORDS,	pyepoll_poll_doc},
+	{NULL,	NULL},
+};
+
+static PyGetSetDef pyepoll_getsetlist[] = {
+	{"closed", (getter)pyepoll_get_closed, NULL,
+	 "True if the epoll handler is closed"},
+	{0},
+};
+
+PyDoc_STRVAR(pyepoll_doc,
+"select.epoll([sizehint=-1])\n\
+\n\
+Returns an epolling object\n\
+\n\
+sizehint must be a positive integer or -1 for the default size. The\n\
+sizehint is used to optimize internal data structures. It doesn't limit\n\
+the maximum number of monitored events.");
+
+static PyTypeObject pyEpoll_Type = {
+	PyVarObject_HEAD_INIT(NULL, 0)
+	"select.epoll",					/* tp_name */
+	sizeof(pyEpoll_Object),				/* tp_basicsize */
+	0,						/* tp_itemsize */
+	(destructor)pyepoll_dealloc,			/* tp_dealloc */
+	0,						/* tp_print */
+	0,						/* tp_getattr */
+	0,						/* tp_setattr */
+	0,						/* tp_compare */
+	0,						/* tp_repr */
+	0,						/* tp_as_number */
+	0,						/* tp_as_sequence */
+	0,						/* tp_as_mapping */
+	0,						/* tp_hash */
+	0,              				/* tp_call */
+	0,						/* tp_str */
+	PyObject_GenericGetAttr,			/* tp_getattro */
+	0,						/* tp_setattro */
+	0,						/* tp_as_buffer */
+	Py_TPFLAGS_DEFAULT,				/* tp_flags */
+	pyepoll_doc,					/* tp_doc */
+	0,						/* tp_traverse */
+	0,						/* tp_clear */
+	0,						/* tp_richcompare */
+	0,						/* tp_weaklistoffset */
+	0,						/* tp_iter */
+	0,						/* tp_iternext */
+	pyepoll_methods,				/* tp_methods */
+	0,						/* tp_members */
+	pyepoll_getsetlist,				/* tp_getset */
+	0,						/* tp_base */
+	0,						/* tp_dict */
+	0,						/* tp_descr_get */
+	0,						/* tp_descr_set */
+	0,						/* tp_dictoffset */
+	0,						/* tp_init */
+	0,						/* tp_alloc */
+	pyepoll_new,					/* tp_new */
+	0,						/* tp_free */
+};
+
+#endif /* HAVE_EPOLL */
+
+#ifdef HAVE_KQUEUE
+/* **************************************************************************
+ *                      kqueue interface for BSD
+ *
+ * Copyright (c) 2000 Doug White, 2006 James Knight, 2007 Christian Heimes
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifdef HAVE_SYS_EVENT_H
+#include <sys/event.h>
+#endif
+
+PyDoc_STRVAR(kqueue_event_doc,
+"kevent(ident, filter=KQ_FILTER_READ, flags=KQ_ADD, fflags=0, data=0, udata=0)\n\
+\n\
+This object is the equivalent of the struct kevent for the C API.\n\
+\n\
+See the kqueue manpage for more detailed information about the meaning\n\
+of the arguments.\n\
+\n\
+One minor note: while you might hope that udata could store a\n\
+reference to a python object, it cannot, because it is impossible to\n\
+keep a proper reference count of the object once it's passed into the\n\
+kernel. Therefore, I have restricted it to only storing an integer.  I\n\
+recommend ignoring it and simply using the 'ident' field to key off\n\
+of. You could also set up a dictionary on the python side to store a\n\
+udata->object mapping.");
+
+typedef struct {
+	PyObject_HEAD
+	struct kevent e;
+} kqueue_event_Object;
+
+static PyTypeObject kqueue_event_Type;
+
+#define kqueue_event_Check(op) (PyObject_TypeCheck((op), &kqueue_event_Type))
+
+typedef struct {
+	PyObject_HEAD
+	SOCKET kqfd;		/* kqueue control fd */
+} kqueue_queue_Object;
+
+static PyTypeObject kqueue_queue_Type;
+
+#define kqueue_queue_Check(op) (PyObject_TypeCheck((op), &kqueue_queue_Type))
+
+/* Unfortunately, we can't store python objects in udata, because
+ * kevents in the kernel can be removed without warning, which would
+ * forever lose the refcount on the object stored with it.
+ */
+
+#define KQ_OFF(x) offsetof(kqueue_event_Object, x)
+static struct PyMemberDef kqueue_event_members[] = {
+	{"ident",	T_UINT,		KQ_OFF(e.ident)},
+	{"filter",	T_SHORT,	KQ_OFF(e.filter)},
+	{"flags",	T_USHORT,	KQ_OFF(e.flags)},
+	{"fflags",	T_UINT,		KQ_OFF(e.fflags)},
+	{"data",	T_INT,		KQ_OFF(e.data)},
+	{"udata",	T_INT,		KQ_OFF(e.udata)},
+	{NULL} /* Sentinel */
+};
+#undef KQ_OFF
+
+static PyObject *
+kqueue_event_repr(kqueue_event_Object *s)
+{
+	char buf[1024];
+	PyOS_snprintf(
+		buf, sizeof(buf),
+		"<select.kevent ident=%lu filter=%d flags=0x%x fflags=0x%x "
+		"data=0x%lx udata=%p>",
+		(unsigned long)(s->e.ident), s->e.filter, s->e.flags,
+		s->e.fflags, (long)(s->e.data), s->e.udata);
+	return PyString_FromString(buf);
+}
+
+static int
+kqueue_event_init(kqueue_event_Object *self, PyObject *args, PyObject *kwds)
+{
+	PyObject *pfd;
+	static char *kwlist[] = {"ident", "filter", "flags", "fflags",
+				 "data", "udata", NULL};
+
+	EV_SET(&(self->e), 0, EVFILT_READ, EV_ADD, 0, 0, 0); /* defaults */
+	
+	if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|hhiii:kevent", kwlist,
+		&pfd, &(self->e.filter), &(self->e.flags),
+		&(self->e.fflags), &(self->e.data), &(self->e.udata))) {
+		return -1;
+	}
+
+	self->e.ident = PyObject_AsFileDescriptor(pfd);
+	if (self->e.ident == -1) {
+		return -1;
+	}
+	return 0;
+}
+
+static PyObject *
+kqueue_event_richcompare(kqueue_event_Object *s, kqueue_event_Object *o,
+			 int op)
+{
+	int result = 0;
+
+	if (!kqueue_event_Check(o)) {
+		if (op == Py_EQ || op == Py_NE) {
+                	PyObject *res = op == Py_EQ ? Py_False : Py_True;
+			Py_INCREF(res);
+			return res;
+		}
+		PyErr_Format(PyExc_TypeError,
+			"can't compare %.200s to %.200s",
+			Py_TYPE(s)->tp_name, Py_TYPE(o)->tp_name);
+		return NULL;
+	}
+	if (((result = s->e.ident - o->e.ident) == 0) &&
+	    ((result = s->e.filter - o->e.filter) == 0) &&
+	    ((result = s->e.flags - o->e.flags) == 0) &&
+	    ((result = s->e.fflags - o->e.fflags) == 0) &&
+	    ((result = s->e.data - o->e.data) == 0) &&
+	    ((result = s->e.udata - o->e.udata) == 0)
+	   ) {
+		result = 0;
+	}
+
+	switch (op) {
+	    case Py_EQ:
+		result = (result == 0);
+		break;
+	    case Py_NE:
+		result = (result != 0);
+		break;
+	    case Py_LE:
+		result = (result <= 0);
+		break;
+	    case Py_GE:
+		result = (result >= 0);
+		break;
+	    case Py_LT:
+		result = (result < 0);
+		break;
+	    case Py_GT:
+		result = (result > 0);
+		break;
+	}
+	return PyBool_FromLong(result);
+}
+
+static PyTypeObject kqueue_event_Type = {
+	PyVarObject_HEAD_INIT(NULL, 0)
+	"select.kevent",				/* tp_name */
+	sizeof(kqueue_event_Object),			/* tp_basicsize */
+	0,						/* tp_itemsize */
+	0,						/* tp_dealloc */
+	0,						/* tp_print */
+	0,						/* tp_getattr */
+	0,						/* tp_setattr */
+	0,						/* tp_compare */
+	(reprfunc)kqueue_event_repr,			/* tp_repr */
+	0,						/* tp_as_number */
+	0,						/* tp_as_sequence */
+	0,						/* tp_as_mapping */
+	0,						/* tp_hash */
+	0,              				/* tp_call */
+	0,						/* tp_str */
+	0,						/* tp_getattro */
+	0,						/* tp_setattro */
+	0,						/* tp_as_buffer */
+	Py_TPFLAGS_DEFAULT,				/* tp_flags */
+	kqueue_event_doc,				/* tp_doc */
+	0,						/* tp_traverse */
+	0,						/* tp_clear */
+	(richcmpfunc)kqueue_event_richcompare,		/* tp_richcompare */
+	0,						/* tp_weaklistoffset */
+	0,						/* tp_iter */
+	0,						/* tp_iternext */
+	0,						/* tp_methods */
+	kqueue_event_members,				/* tp_members */
+	0,						/* tp_getset */
+	0,						/* tp_base */
+	0,						/* tp_dict */
+	0,						/* tp_descr_get */
+	0,						/* tp_descr_set */
+	0,						/* tp_dictoffset */
+	(initproc)kqueue_event_init,			/* tp_init */
+	0,						/* tp_alloc */
+	0,						/* tp_new */
+	0,						/* tp_free */
+};
+
+static PyObject *
+kqueue_queue_err_closed(void)
+{
+	PyErr_SetString(PyExc_ValueError, "I/O operation on closed kqueue fd");
+	return NULL;
+}
+
+static int
+kqueue_queue_internal_close(kqueue_queue_Object *self)
+{
+	int save_errno = 0;
+	if (self->kqfd >= 0) {
+		int kqfd = self->kqfd;
+		self->kqfd = -1;
+		Py_BEGIN_ALLOW_THREADS
+		if (close(kqfd) < 0)
+			save_errno = errno;
+		Py_END_ALLOW_THREADS
+	}
+	return save_errno;
+}
+
+static PyObject *
+newKqueue_Object(PyTypeObject *type, SOCKET fd)
+{
+	kqueue_queue_Object *self;
+	assert(type != NULL && type->tp_alloc != NULL);
+	self = (kqueue_queue_Object *) type->tp_alloc(type, 0);
+	if (self == NULL) {
+		return NULL;
+	}
+	
+	if (fd == -1) {
+		Py_BEGIN_ALLOW_THREADS
+		self->kqfd = kqueue();
+		Py_END_ALLOW_THREADS
+	}
+	else {
+		self->kqfd = fd;
+	}
+	if (self->kqfd < 0) {
+		Py_DECREF(self);
+		PyErr_SetFromErrno(PyExc_IOError);
+		return NULL;
+	}
+	return (PyObject *)self;
+}
+
+static PyObject *
+kqueue_queue_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
+{
+
+	if ((args != NULL && PyObject_Size(args)) ||
+			(kwds != NULL && PyObject_Size(kwds))) {
+		PyErr_SetString(PyExc_ValueError,
+				"select.kqueue doesn't accept arguments");
+		return NULL;
+	}
+
+	return newKqueue_Object(type, -1);
+}
+
+static void
+kqueue_queue_dealloc(kqueue_queue_Object *self)
+{
+	kqueue_queue_internal_close(self);
+	Py_TYPE(self)->tp_free(self);
+}
+
+static PyObject*
+kqueue_queue_close(kqueue_queue_Object *self)
+{
+	errno = kqueue_queue_internal_close(self);
+	if (errno < 0) {
+		PyErr_SetFromErrno(PyExc_IOError);
+		return NULL;
+	}
+	Py_RETURN_NONE;
+}
+
+PyDoc_STRVAR(kqueue_queue_close_doc,
+"close() -> None\n\
+\n\
+Close the kqueue control file descriptor. Further operations on the kqueue\n\
+object will raise an exception.");
+
+static PyObject*
+kqueue_queue_get_closed(kqueue_queue_Object *self)
+{
+	if (self->kqfd < 0)
+		Py_RETURN_TRUE;
+	else
+		Py_RETURN_FALSE;
+}
+
+static PyObject*
+kqueue_queue_fileno(kqueue_queue_Object *self)
+{
+	if (self->kqfd < 0)
+		return kqueue_queue_err_closed();
+	return PyInt_FromLong(self->kqfd);
+}
+
+PyDoc_STRVAR(kqueue_queue_fileno_doc,
+"fileno() -> int\n\
+\n\
+Return the kqueue control file descriptor.");
+
+static PyObject*
+kqueue_queue_fromfd(PyObject *cls, PyObject *args)
+{
+	SOCKET fd;
+
+	if (!PyArg_ParseTuple(args, "i:fromfd", &fd))
+		return NULL;
+
+	return newKqueue_Object((PyTypeObject*)cls, fd);
+}
+
+PyDoc_STRVAR(kqueue_queue_fromfd_doc,
+"fromfd(fd) -> kqueue\n\
+\n\
+Create a kqueue object from a given control fd.");
+
+static PyObject *
+kqueue_queue_control(kqueue_queue_Object *self, PyObject *args)
+{
+	int nevents = 0;
+	int gotevents = 0;
+	int nchanges = 0;
+	int i = 0;
+	PyObject *otimeout = NULL;
+	PyObject *ch = NULL;
+	PyObject *it = NULL, *ei = NULL;
+	PyObject *result = NULL;
+	struct kevent *evl = NULL;
+	struct kevent *chl = NULL;
+	struct timespec timeoutspec;
+	struct timespec *ptimeoutspec;
+
+	if (self->kqfd < 0)
+		return kqueue_queue_err_closed();
+
+	if (!PyArg_ParseTuple(args, "Oi|O:control", &ch, &nevents, &otimeout))
+		return NULL;
+
+	if (nevents < 0) {
+		PyErr_Format(PyExc_ValueError,
+			"Length of eventlist must be 0 or positive, got %d",
+			nchanges);
+		return NULL;
+	}
+
+	if (ch != NULL && ch != Py_None) {
+		it = PyObject_GetIter(ch);
+		if (it == NULL) {
+			PyErr_SetString(PyExc_TypeError,
+					"changelist is not iterable");
+			return NULL;
+		}
+		nchanges = PyObject_Size(ch);
+		if (nchanges < 0) {
+			return NULL;
+		}
+	}
+
+	if (otimeout == Py_None || otimeout == NULL) {
+		ptimeoutspec = NULL;
+	}
+	else if (PyNumber_Check(otimeout)) {
+		double timeout;
+		long seconds;
+
+		timeout = PyFloat_AsDouble(otimeout);
+		if (timeout == -1 && PyErr_Occurred())
+			return NULL;
+		if (timeout > (double)LONG_MAX) {
+			PyErr_SetString(PyExc_OverflowError,
+					"timeout period too long");
+			return NULL;
+		}
+		if (timeout < 0) {
+			PyErr_SetString(PyExc_ValueError,
+					"timeout must be positive or None");
+			return NULL;
+		}
+
+		seconds = (long)timeout;
+		timeout = timeout - (double)seconds;
+		timeoutspec.tv_sec = seconds;
+		timeoutspec.tv_nsec = (long)(timeout * 1E9);
+		ptimeoutspec = &timeoutspec;
+	}
+	else {
+		PyErr_Format(PyExc_TypeError,
+			"timeout argument must be an number "
+			"or None, got %.200s",
+			Py_TYPE(otimeout)->tp_name);
+		return NULL;
+	}
+
+	if (nchanges) {
+		chl = PyMem_New(struct kevent, nchanges);
+		if (chl == NULL) {
+			PyErr_NoMemory();
+			return NULL;
+		}
+		while ((ei = PyIter_Next(it)) != NULL) {
+			if (!kqueue_event_Check(ei)) {
+				Py_DECREF(ei);
+				PyErr_SetString(PyExc_TypeError,
+					"changelist must be an iterable of "
+				 	"select.kevent objects");
+				goto error;
+			} else {
+				chl[i] = ((kqueue_event_Object *)ei)->e;
+			}
+			Py_DECREF(ei);
+		}
+	}
+	Py_CLEAR(it);
+
+	/* event list */
+	if (nevents) {
+		evl = PyMem_New(struct kevent, nevents);
+		if (evl == NULL) {
+			PyErr_NoMemory();
+			return NULL;
+		}
+	}
+
+	Py_BEGIN_ALLOW_THREADS
+	gotevents = kevent(self->kqfd, chl, nchanges,
+			   evl, nevents, ptimeoutspec);
+	Py_END_ALLOW_THREADS
+
+	if (gotevents == -1) {
+		PyErr_SetFromErrno(PyExc_OSError);
+		goto error;
+	}
+
+	result = PyList_New(gotevents);
+	if (result == NULL) {
+		goto error;
+	}
+
+	for (i=0; i < gotevents; i++) {
+		kqueue_event_Object *ch;
+
+		ch = PyObject_New(kqueue_event_Object, &kqueue_event_Type);
+		if (ch == NULL) {
+			goto error;
+		}
+		ch->e = evl[i];
+		PyList_SET_ITEM(result, i, (PyObject *)ch);
+	}
+	PyMem_Free(chl);
+	PyMem_Free(evl);
+	return result;
+
+    error:
+	PyMem_Free(chl);
+	PyMem_Free(evl);
+	Py_XDECREF(result);
+	Py_XDECREF(it);
+	return NULL;
+}
+
+PyDoc_STRVAR(kqueue_queue_control_doc,
+"control(changelist, max_events=0[, timeout=None]) -> eventlist\n\
+\n\
+Calls the kernel kevent function.\n\
+- changelist must be a list of kevent objects describing the changes\n\
+  to be made to the kernel's watch list or None.\n\
+- max_events lets you specify the maximum number of events that the\n\
+  kernel will return.\n\
+- timeout is the maximum time to wait in seconds, or else None,\n\
+  to wait forever. timeout accepts floats for smaller timeouts, too.");
+
+
+static PyMethodDef kqueue_queue_methods[] = {
+	{"fromfd",	(PyCFunction)kqueue_queue_fromfd,
+	 METH_VARARGS | METH_CLASS, kqueue_queue_fromfd_doc},
+	{"close",	(PyCFunction)kqueue_queue_close,	METH_NOARGS,
+	 kqueue_queue_close_doc},
+	{"fileno",	(PyCFunction)kqueue_queue_fileno,	METH_NOARGS,
+	 kqueue_queue_fileno_doc},
+	{"control",	(PyCFunction)kqueue_queue_control,
+	 METH_VARARGS ,	kqueue_queue_control_doc},
+	{NULL,	NULL},
+};
+
+static PyGetSetDef kqueue_queue_getsetlist[] = {
+	{"closed", (getter)kqueue_queue_get_closed, NULL,
+	 "True if the kqueue handler is closed"},
+	{0},
+};
+
+PyDoc_STRVAR(kqueue_queue_doc,
+"Kqueue syscall wrapper.\n\
+\n\
+For example, to start watching a socket for input:\n\
+>>> kq = kqueue()\n\
+>>> sock = socket()\n\
+>>> sock.connect((host, port))\n\
+>>> kq.control([kevent(sock, KQ_FILTER_WRITE, KQ_EV_ADD)], 0)\n\
+\n\
+To wait one second for it to become writeable:\n\
+>>> kq.control(None, 1, 1000)\n\
+\n\
+To stop listening:\n\
+>>> kq.control([kevent(sock, KQ_FILTER_WRITE, KQ_EV_DELETE)], 0)");
+
+static PyTypeObject kqueue_queue_Type = {
+	PyVarObject_HEAD_INIT(NULL, 0)
+	"select.kqueue",				/* tp_name */
+	sizeof(kqueue_queue_Object),			/* tp_basicsize */
+	0,						/* tp_itemsize */
+	(destructor)kqueue_queue_dealloc,		/* tp_dealloc */
+	0,						/* tp_print */
+	0,						/* tp_getattr */
+	0,						/* tp_setattr */
+	0,						/* tp_compare */
+	0,						/* tp_repr */
+	0,						/* tp_as_number */
+	0,						/* tp_as_sequence */
+	0,						/* tp_as_mapping */
+	0,						/* tp_hash */
+	0,              				/* tp_call */
+	0,						/* tp_str */
+	0,						/* tp_getattro */
+	0,						/* tp_setattro */
+	0,						/* tp_as_buffer */
+	Py_TPFLAGS_DEFAULT,				/* tp_flags */
+	kqueue_queue_doc,				/* tp_doc */
+	0,						/* tp_traverse */
+	0,						/* tp_clear */
+	0,						/* tp_richcompare */
+	0,						/* tp_weaklistoffset */
+	0,						/* tp_iter */
+	0,						/* tp_iternext */
+	kqueue_queue_methods,				/* tp_methods */
+	0,						/* tp_members */
+	kqueue_queue_getsetlist,			/* tp_getset */
+	0,						/* tp_base */
+	0,						/* tp_dict */
+	0,						/* tp_descr_get */
+	0,						/* tp_descr_set */
+	0,						/* tp_dictoffset */
+	0,						/* tp_init */
+	0,						/* tp_alloc */
+	kqueue_queue_new,				/* tp_new */
+	0,						/* tp_free */
+};
+
+#endif /* HAVE_KQUEUE */
+/* ************************************************************************ */
+
 PyDoc_STRVAR(select_doc,
 "select(rlist, wlist, xlist[, timeout]) -> (rlist, wlist, xlist)\n\
 \n\
@@ -667,14 +1718,15 @@
 that are ready.\n\
 \n\
 *** IMPORTANT NOTICE ***\n\
-On Windows and OpenVMS, only sockets are supported; on Unix, all file descriptors.");
+On Windows and OpenVMS, only sockets are supported; on Unix, all file\n\
+descriptors.");
 
 static PyMethodDef select_methods[] = {
-    {"select",	select_select, METH_VARARGS, select_doc},
-#if defined(HAVE_POLL) 
-    {"poll",    select_poll,   METH_NOARGS, poll_doc},
+	{"select",	select_select,	METH_VARARGS,	select_doc},
+#ifdef HAVE_POLL
+	{"poll",	select_poll,	METH_NOARGS,	poll_doc},
 #endif /* HAVE_POLL */
-    {0,  	0},			     /* sentinel */
+	{0,  	0},	/* sentinel */
 };
 
 PyDoc_STRVAR(module_doc,
@@ -694,8 +1746,8 @@
 	SelectError = PyErr_NewException("select.error", NULL, NULL);
 	Py_INCREF(SelectError);
 	PyModule_AddObject(m, "error", SelectError);
-#if defined(HAVE_POLL) 
 
+#if defined(HAVE_POLL)
 #ifdef __APPLE__
 	if (select_have_broken_poll()) {
 		if (PyObject_DelAttrString(m, "poll") == -1) {
@@ -730,4 +1782,103 @@
 #endif
 	}
 #endif /* HAVE_POLL */
+
+#ifdef HAVE_EPOLL
+	Py_TYPE(&pyEpoll_Type) = &PyType_Type;
+	if (PyType_Ready(&pyEpoll_Type) < 0)
+		return;
+
+	Py_INCREF(&pyEpoll_Type);
+	PyModule_AddObject(m, "epoll", (PyObject *) &pyEpoll_Type);
+
+	PyModule_AddIntConstant(m, "EPOLLIN", EPOLLIN);
+	PyModule_AddIntConstant(m, "EPOLLOUT", EPOLLOUT);
+	PyModule_AddIntConstant(m, "EPOLLPRI", EPOLLPRI);
+	PyModule_AddIntConstant(m, "EPOLLERR", EPOLLERR);
+	PyModule_AddIntConstant(m, "EPOLLHUP", EPOLLHUP);
+	PyModule_AddIntConstant(m, "EPOLLET", EPOLLET);
+#ifdef EPOLLONESHOT
+	/* Kernel 2.6.2+ */
+	PyModule_AddIntConstant(m, "EPOLLONESHOT", EPOLLONESHOT);
+#endif
+	/* PyModule_AddIntConstant(m, "EPOLL_RDHUP", EPOLLRDHUP); */
+	PyModule_AddIntConstant(m, "EPOLLRDNORM", EPOLLRDNORM);
+	PyModule_AddIntConstant(m, "EPOLLRDBAND", EPOLLRDBAND);
+	PyModule_AddIntConstant(m, "EPOLLWRNORM", EPOLLWRNORM);
+	PyModule_AddIntConstant(m, "EPOLLWRBAND", EPOLLWRBAND);
+	PyModule_AddIntConstant(m, "EPOLLMSG", EPOLLMSG);
+#endif /* HAVE_EPOLL */
+
+#ifdef HAVE_KQUEUE
+	kqueue_event_Type.tp_new = PyType_GenericNew;
+	Py_TYPE(&kqueue_event_Type) = &PyType_Type;
+	if(PyType_Ready(&kqueue_event_Type) < 0)
+		return;
+
+	Py_INCREF(&kqueue_event_Type);
+	PyModule_AddObject(m, "kevent", (PyObject *)&kqueue_event_Type);
+
+	Py_TYPE(&kqueue_queue_Type) = &PyType_Type;
+	if(PyType_Ready(&kqueue_queue_Type) < 0)
+		return;
+	Py_INCREF(&kqueue_queue_Type);
+	PyModule_AddObject(m, "kqueue", (PyObject *)&kqueue_queue_Type);
+	
+	/* event filters */
+	PyModule_AddIntConstant(m, "KQ_FILTER_READ", EVFILT_READ);
+	PyModule_AddIntConstant(m, "KQ_FILTER_WRITE", EVFILT_WRITE);
+	PyModule_AddIntConstant(m, "KQ_FILTER_AIO", EVFILT_AIO);
+	PyModule_AddIntConstant(m, "KQ_FILTER_VNODE", EVFILT_VNODE);
+	PyModule_AddIntConstant(m, "KQ_FILTER_PROC", EVFILT_PROC);
+#ifdef EVFILT_NETDEV
+	PyModule_AddIntConstant(m, "KQ_FILTER_NETDEV", EVFILT_NETDEV);
+#endif
+	PyModule_AddIntConstant(m, "KQ_FILTER_SIGNAL", EVFILT_SIGNAL);
+	PyModule_AddIntConstant(m, "KQ_FILTER_TIMER", EVFILT_TIMER);
+
+	/* event flags */
+	PyModule_AddIntConstant(m, "KQ_EV_ADD", EV_ADD);
+	PyModule_AddIntConstant(m, "KQ_EV_DELETE", EV_DELETE);
+	PyModule_AddIntConstant(m, "KQ_EV_ENABLE", EV_ENABLE);
+	PyModule_AddIntConstant(m, "KQ_EV_DISABLE", EV_DISABLE);
+	PyModule_AddIntConstant(m, "KQ_EV_ONESHOT", EV_ONESHOT);
+	PyModule_AddIntConstant(m, "KQ_EV_CLEAR", EV_CLEAR);
+
+	PyModule_AddIntConstant(m, "KQ_EV_SYSFLAGS", EV_SYSFLAGS);
+	PyModule_AddIntConstant(m, "KQ_EV_FLAG1", EV_FLAG1);
+
+	PyModule_AddIntConstant(m, "KQ_EV_EOF", EV_EOF);
+	PyModule_AddIntConstant(m, "KQ_EV_ERROR", EV_ERROR);
+
+	/* READ WRITE filter flag */
+	PyModule_AddIntConstant(m, "KQ_NOTE_LOWAT", NOTE_LOWAT);
+	
+	/* VNODE filter flags  */
+	PyModule_AddIntConstant(m, "KQ_NOTE_DELETE", NOTE_DELETE);
+	PyModule_AddIntConstant(m, "KQ_NOTE_WRITE", NOTE_WRITE);
+	PyModule_AddIntConstant(m, "KQ_NOTE_EXTEND", NOTE_EXTEND);
+	PyModule_AddIntConstant(m, "KQ_NOTE_ATTRIB", NOTE_ATTRIB);
+	PyModule_AddIntConstant(m, "KQ_NOTE_LINK", NOTE_LINK);
+	PyModule_AddIntConstant(m, "KQ_NOTE_RENAME", NOTE_RENAME);
+	PyModule_AddIntConstant(m, "KQ_NOTE_REVOKE", NOTE_REVOKE);
+
+	/* PROC filter flags  */
+	PyModule_AddIntConstant(m, "KQ_NOTE_EXIT", NOTE_EXIT);
+	PyModule_AddIntConstant(m, "KQ_NOTE_FORK", NOTE_FORK);
+	PyModule_AddIntConstant(m, "KQ_NOTE_EXEC", NOTE_EXEC);
+	PyModule_AddIntConstant(m, "KQ_NOTE_PCTRLMASK", NOTE_PCTRLMASK);
+	PyModule_AddIntConstant(m, "KQ_NOTE_PDATAMASK", NOTE_PDATAMASK);
+
+	PyModule_AddIntConstant(m, "KQ_NOTE_TRACK", NOTE_TRACK);
+	PyModule_AddIntConstant(m, "KQ_NOTE_CHILD", NOTE_CHILD);
+	PyModule_AddIntConstant(m, "KQ_NOTE_TRACKERR", NOTE_TRACKERR);
+
+	/* NETDEV filter flags */
+#ifdef EVFILT_NETDEV
+	PyModule_AddIntConstant(m, "KQ_NOTE_LINKUP", NOTE_LINKUP);
+	PyModule_AddIntConstant(m, "KQ_NOTE_LINKDOWN", NOTE_LINKDOWN);
+	PyModule_AddIntConstant(m, "KQ_NOTE_LINKINV", NOTE_LINKINV);
+#endif
+
+#endif /* HAVE_KQUEUE */
 }

Modified: python/trunk/configure
==============================================================================
--- python/trunk/configure	(original)
+++ python/trunk/configure	Sat Mar 22 00:49:44 2008
@@ -5419,13 +5419,15 @@
 
 
 
+
+
 for ac_header in asm/types.h conio.h curses.h direct.h dlfcn.h errno.h \
 fcntl.h grp.h \
 io.h langinfo.h libintl.h ncurses.h poll.h process.h pthread.h \
 shadow.h signal.h stdint.h stropts.h termios.h thread.h \
 unistd.h utime.h \
-sys/audioio.h sys/bsdtty.h sys/file.h sys/loadavg.h sys/lock.h sys/mkdev.h \
-sys/modem.h \
+sys/audioio.h sys/bsdtty.h sys/epoll.h sys/event.h sys/file.h sys/loadavg.h \
+sys/lock.h sys/mkdev.h sys/modem.h \
 sys/param.h sys/poll.h sys/select.h sys/socket.h sys/statvfs.h sys/stat.h \
 sys/time.h \
 sys/times.h sys/types.h sys/un.h sys/utsname.h sys/wait.h pty.h libutil.h \
@@ -15917,7 +15919,111 @@
 fi
 
 rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+{ echo "$as_me:$LINENO: checking for epoll" >&5
+echo $ECHO_N "checking for epoll... $ECHO_C" >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+#include <sys/epoll.h>
+int
+main ()
+{
+void *x=epoll_create
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+  (eval "$ac_compile") 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } && {
+	 test -z "$ac_c_werror_flag" ||
+	 test ! -s conftest.err
+       } && test -s conftest.$ac_objext; then
+
+cat >>confdefs.h <<\_ACEOF
+#define HAVE_EPOLL 1
+_ACEOF
+
+  { echo "$as_me:$LINENO: result: yes" >&5
+echo "${ECHO_T}yes" >&6; }
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+	{ echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6; }
+
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+{ echo "$as_me:$LINENO: checking for kqueue" >&5
+echo $ECHO_N "checking for kqueue... $ECHO_C" >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+
+#include <sys/types.h>
+#include <sys/event.h>
+
+int
+main ()
+{
+int x=kqueue()
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+  (eval "$ac_compile") 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } && {
+	 test -z "$ac_c_werror_flag" ||
+	 test ! -s conftest.err
+       } && test -s conftest.$ac_objext; then
+
+cat >>confdefs.h <<\_ACEOF
+#define HAVE_KQUEUE 1
+_ACEOF
 
+  { echo "$as_me:$LINENO: result: yes" >&5
+echo "${ECHO_T}yes" >&6; }
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+	{ echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6; }
+
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
 # On some systems (eg. FreeBSD 5), we would find a definition of the
 # functions ctermid_r, setgroups in the library, but no prototype
 # (e.g. because we use _XOPEN_SOURCE). See whether we can take their

Modified: python/trunk/configure.in
==============================================================================
--- python/trunk/configure.in	(original)
+++ python/trunk/configure.in	Sat Mar 22 00:49:44 2008
@@ -1102,8 +1102,8 @@
 io.h langinfo.h libintl.h ncurses.h poll.h process.h pthread.h \
 shadow.h signal.h stdint.h stropts.h termios.h thread.h \
 unistd.h utime.h \
-sys/audioio.h sys/bsdtty.h sys/file.h sys/loadavg.h sys/lock.h sys/mkdev.h \
-sys/modem.h \
+sys/audioio.h sys/bsdtty.h sys/epoll.h sys/event.h sys/file.h sys/loadavg.h \
+sys/lock.h sys/mkdev.h sys/modem.h \
 sys/param.h sys/poll.h sys/select.h sys/socket.h sys/statvfs.h sys/stat.h \
 sys/time.h \
 sys/times.h sys/types.h sys/un.h sys/utsname.h sys/wait.h pty.h libutil.h \
@@ -2354,7 +2354,21 @@
   AC_MSG_RESULT(yes),
   AC_MSG_RESULT(no)
 )
-
+AC_MSG_CHECKING(for epoll)
+AC_TRY_COMPILE([#include <sys/epoll.h>], void *x=epoll_create,
+  AC_DEFINE(HAVE_EPOLL, 1, Define if you have the 'epoll' functions.)
+  AC_MSG_RESULT(yes),
+  AC_MSG_RESULT(no)
+)
+AC_MSG_CHECKING(for kqueue)
+AC_TRY_COMPILE([
+#include <sys/types.h>
+#include <sys/event.h>
+    ], int x=kqueue(),
+  AC_DEFINE(HAVE_KQUEUE, 1, Define if you have the 'kqueue' functions.)
+  AC_MSG_RESULT(yes),
+  AC_MSG_RESULT(no)
+)
 # On some systems (eg. FreeBSD 5), we would find a definition of the
 # functions ctermid_r, setgroups in the library, but no prototype
 # (e.g. because we use _XOPEN_SOURCE). See whether we can take their

Modified: python/trunk/pyconfig.h.in
==============================================================================
--- python/trunk/pyconfig.h.in	(original)
+++ python/trunk/pyconfig.h.in	Sat Mar 22 00:49:44 2008
@@ -147,6 +147,9 @@
 /* Defined when any dynamic module loading is enabled. */
 #undef HAVE_DYNAMIC_LOADING
 
+/* Define if you have the 'epoll' functions. */
+#undef HAVE_EPOLL
+
 /* Define to 1 if you have the <errno.h> header file. */
 #undef HAVE_ERRNO_H
 
@@ -318,6 +321,9 @@
 /* Define to 1 if you have the `killpg' function. */
 #undef HAVE_KILLPG
 
+/* Define if you have the 'kqueue' functions. */
+#undef HAVE_KQUEUE
+
 /* Define to 1 if you have the <langinfo.h> header file. */
 #undef HAVE_LANGINFO_H
 
@@ -627,6 +633,12 @@
    */
 #undef HAVE_SYS_DIR_H
 
+/* Define to 1 if you have the <sys/epoll.h> header file. */
+#undef HAVE_SYS_EPOLL_H
+
+/* Define to 1 if you have the <sys/event.h> header file. */
+#undef HAVE_SYS_EVENT_H
+
 /* Define to 1 if you have the <sys/file.h> header file. */
 #undef HAVE_SYS_FILE_H
 


More information about the Python-checkins mailing list