diff options
-rw-r--r-- | Doc/ACKS.txt | 1 | ||||
-rw-r--r-- | Doc/c-api/import.rst | 13 | ||||
-rw-r--r-- | Doc/library/__future__.rst | 5 | ||||
-rw-r--r-- | Doc/library/future_builtins.rst | 53 | ||||
-rw-r--r-- | Doc/library/python.rst | 1 | ||||
-rw-r--r-- | Doc/library/select.rst | 304 | ||||
-rw-r--r-- | Doc/license.rst | 55 | ||||
-rw-r--r-- | Doc/reference/datamodel.rst | 4 | ||||
-rw-r--r-- | Doc/tutorial/datastructures.rst | 9 | ||||
-rw-r--r-- | Doc/whatsnew/2.6.rst | 4 | ||||
-rw-r--r-- | Lib/csv.py | 3 | ||||
-rwxr-xr-x | Lib/test/regrtest.py | 28 | ||||
-rw-r--r-- | Lib/test/test_epoll.py | 189 | ||||
-rw-r--r-- | Lib/test/test_kqueue.py | 166 | ||||
-rw-r--r-- | Lib/test/test_poll.py | 3 | ||||
-rw-r--r-- | Lib/test/test_signal.py | 241 | ||||
-rw-r--r-- | Lib/test/test_threading.py | 46 | ||||
-rw-r--r-- | Lib/test/test_urllib2.py | 8 | ||||
-rw-r--r-- | Lib/urllib2.py | 7 | ||||
-rw-r--r-- | Modules/selectmodule.c | 1175 | ||||
-rwxr-xr-x | configure | 110 | ||||
-rw-r--r-- | configure.in | 20 | ||||
-rw-r--r-- | pyconfig.h.in | 12 |
23 files changed, 2280 insertions, 177 deletions
diff --git a/Doc/ACKS.txt b/Doc/ACKS.txt index 5e97e33..9eae6a3 100644 --- a/Doc/ACKS.txt +++ b/Doc/ACKS.txt @@ -21,6 +21,7 @@ docs@python.org), and we'll be glad to correct the problem. * Chris Barker * Don Bashford * Anthony Baxter + * Alexander Belopolsky * Bennett Benson * Jonathan Black * Robin Boerdijk diff --git a/Doc/c-api/import.rst b/Doc/c-api/import.rst index 907f531..89d5f84 100644 --- a/Doc/c-api/import.rst +++ b/Doc/c-api/import.rst @@ -134,6 +134,19 @@ Importing Modules ``sys.modules``). Note that this is a per-interpreter variable. +.. cfunction:: PyObject* PyImport_GetImporter(PyObject *path) + + Return an importer object for a :data:`sys.path`/:attr:`pkg.__path__` item + *path*, possibly by fetching it from the :data:`sys.path_importer_cache` + dict. If it wasn't yet cached, traverse :data:`sys.path_hooks` until a hook + is found that can handle the path item. Return ``None`` if no hook could; + this tells our caller it should fall back to the builtin import mechanism. + Cache the result in :data:`sys.path_importer_cache`. Return a new reference + to the importer object. + + .. versionadded:: 2.6 + + .. cfunction:: void _PyImport_Init() Initialize the import mechanism. For internal use only. diff --git a/Doc/library/__future__.rst b/Doc/library/__future__.rst index 6bf2830..e2349cc 100644 --- a/Doc/library/__future__.rst +++ b/Doc/library/__future__.rst @@ -1,4 +1,3 @@ - :mod:`__future__` --- Future statement definitions ================================================== @@ -22,8 +21,8 @@ Each statement in :file:`__future__.py` is of the form:: - FeatureName = "_Feature(" OptionalRelease "," MandatoryRelease "," - CompilerFlag ")" + FeatureName = _Feature(OptionalRelease, MandatoryRelease, + CompilerFlag) where, normally, *OptionalRelease* is less than *MandatoryRelease*, and both are diff --git a/Doc/library/future_builtins.rst b/Doc/library/future_builtins.rst new file mode 100644 index 0000000..e845fe4 --- /dev/null +++ b/Doc/library/future_builtins.rst @@ -0,0 +1,53 @@ +:mod:`future_builtins` --- Python 3 builtins +============================================ + +.. module:: future_builtins +.. sectionauthor:: Georg Brandl +.. versionadded:: 2.6 + +This module provides functions that exist in 2.x, but have different behavior in +Python 3, so they cannot be put into the 2.x builtin namespace. + +Instead, if you want to write code compatible with Python 3 builtins, import +them from this module, like this:: + + from future_builtins import map, filter + + ... code using Python 3-style map and filter ... + +The :program:`2to3` tool that ports Python 2 code to Python 3 will recognize +this usage and leave the new builtins alone. + +.. note:: + + The Python 3 :func:`print` function is already in the builtins, but cannot be + accessed from Python 2 code unless you use the appropriate future statement:: + + from __future__ import print_function + + +Available builtins are: + +.. function:: filter(function, iterable) + + Works like :func:`itertools.ifilter`. + +.. function:: hex(object) + + Works like the builtin :func:`hex`, but instead of :meth:`__hex__` it will + use the :meth:`__index__` method on its argument to get an integer that is + then converted to hexadecimal. + +.. function:: map(function, iterable, ...) + + Works like :func:`itertools.imap`. + +.. function:: oct(object) + + Works like the builtin :func:`oct`, but instead of :meth:`__oct__` it will + use the :meth:`__index__` method on its argument to get an integer that is + then converted to hexadecimal. + +.. function:: zip(*iterables) + + Works like :func:`itertools.izip`. diff --git a/Doc/library/python.rst b/Doc/library/python.rst index 0fdfa53..379d508 100644 --- a/Doc/library/python.rst +++ b/Doc/library/python.rst @@ -14,6 +14,7 @@ overview: sys.rst builtins.rst + future_builtins.rst __main__.rst warnings.rst contextlib.rst diff --git a/Doc/library/select.rst b/Doc/library/select.rst index 4a97179..1368c78 100644 --- a/Doc/library/select.rst +++ b/Doc/library/select.rst @@ -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 @@ The module defines the following: 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 @@ The module defines the following: 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 @@ The module defines the following: 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 @@ linearly scanned again. :cfunc:`select` is O(highest file descriptor), while 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 @@ linearly scanned again. :cfunc:`select` is O(highest file descriptor), while 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 diff --git a/Doc/license.rst b/Doc/license.rst index 0226ec5..e10f930 100644 --- a/Doc/license.rst +++ b/Doc/license.rst @@ -599,3 +599,58 @@ The :mod:`xmlrpclib` module contains the following notice:: 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. diff --git a/Doc/reference/datamodel.rst b/Doc/reference/datamodel.rst index 0ab5f11..2ff9400 100644 --- a/Doc/reference/datamodel.rst +++ b/Doc/reference/datamodel.rst @@ -510,6 +510,10 @@ Callable types An instance method object combines a class, a class instance and any callable object (normally a user-defined function). + .. versionchanged:: 2.6 + For 3.0 forward-compatibility, :attr:`im_func` is also available as + :attr:`__func__`, and :attr:`im_self` as :attr:`__self__`. + .. index:: single: __func__ (method attribute) single: __self__ (method attribute) diff --git a/Doc/tutorial/datastructures.rst b/Doc/tutorial/datastructures.rst index e2f218c..201fa72 100644 --- a/Doc/tutorial/datastructures.rst +++ b/Doc/tutorial/datastructures.rst @@ -83,17 +83,20 @@ objects: .. method:: list.append(x) + :noindex: Add an item to the end of the list; equivalent to ``a[len(a):] = [x]``. .. method:: list.extend(L) + :noindex: Extend the list by appending all the items in the given list; equivalent to ``a[len(a):] = L``. .. method:: list.insert(i, x) + :noindex: Insert an item at a given position. The first argument is the index of the element before which to insert, so ``a.insert(0, x)`` inserts at the front of @@ -101,12 +104,14 @@ objects: .. method:: list.remove(x) + :noindex: Remove the first item from the list whose value is *x*. It is an error if there is no such item. .. method:: list.pop([i]) + :noindex: Remove the item at the given position in the list, and return it. If no index is specified, ``a.pop()`` removes and returns the last item in the list. (The @@ -116,22 +121,26 @@ objects: .. method:: list.index(x) + :noindex: Return the index in the list of the first item whose value is *x*. It is an error if there is no such item. .. method:: list.count(x) + :noindex: Return the number of times *x* appears in the list. .. method:: list.sort() + :noindex: Sort the items of the list, in place. .. method:: list.reverse() + :noindex: Reverse the elements of the list, in place. diff --git a/Doc/whatsnew/2.6.rst b/Doc/whatsnew/2.6.rst index 3378fc8..0e8901b 100644 --- a/Doc/whatsnew/2.6.rst +++ b/Doc/whatsnew/2.6.rst @@ -540,7 +540,7 @@ complicated expressions inside a format string. So far we've shown how to specify which field to substitute into the resulting string. The precise formatting used is also controllable by -adding a colon followed by a format specifier. For example: +adding a colon followed by a format specifier. For example:: # Field 0: left justify, pad to 15 characters # Field 1: right justify, pad to 6 characters @@ -552,7 +552,7 @@ adding a colon followed by a format specifier. For example: fmt.format('Banquet', 125) -> 'Banquet $ 125' -Format specifiers can reference other fields through nesting: +Format specifiers can reference other fields through nesting:: fmt = '{0:{1}}' fmt.format('Invoice #1234', width) -> @@ -72,6 +72,8 @@ class DictReader: self.restkey = restkey # key to catch long rows self.restval = restval # default value for short rows self.reader = reader(f, dialect, *args, **kwds) + self.dialect = dialect + self.line_num = 0 def __iter__(self): return self @@ -81,6 +83,7 @@ class DictReader: if self.fieldnames is None: self.fieldnames = row row = next(self.reader) + self.line_num = self.reader.line_num # unlike the basic reader, we prefer not to return blanks, # because we will typically wind up with a dict full of None diff --git a/Lib/test/regrtest.py b/Lib/test/regrtest.py index 2212af5..9b8b572 100755 --- a/Lib/test/regrtest.py +++ b/Lib/test/regrtest.py @@ -873,10 +873,12 @@ _expectations = { test_dl test_fcntl test_fork1 + test_epoll test_gdbm test_grp test_ioctl test_largefile + test_kqueue test_mhlib test_openpty test_ossaudiodev @@ -897,6 +899,7 @@ _expectations = { test_curses test_dl test_largefile + test_kqueue test_ossaudiodev """, 'mac': @@ -912,10 +915,12 @@ _expectations = { test_dl test_fcntl test_fork1 + test_epoll test_grp test_ioctl test_largefile test_locale + test_kqueue test_mmap test_openpty test_ossaudiodev @@ -933,7 +938,9 @@ _expectations = { """ test_bsddb test_dl + test_epoll test_largefile + test_kqueue test_minidom test_openpty test_pyexpat @@ -944,7 +951,9 @@ _expectations = { """ test_bsddb test_dl + test_epoll test_largefile + test_kqueue test_minidom test_openpty test_pyexpat @@ -957,9 +966,11 @@ _expectations = { test_bsddb test_dl test_fork1 + test_epoll test_gettext test_largefile test_locale + test_kqueue test_minidom test_openpty test_pyexpat @@ -977,9 +988,11 @@ _expectations = { test_bsddb test_bsddb3 test_curses + test_epoll test_gdbm test_largefile test_locale + test_minidom test_ossaudiodev test_poll """, @@ -988,6 +1001,8 @@ _expectations = { test_bsddb test_curses test_dbm + test_epoll + test_kqueue test_gdbm test_gzip test_openpty @@ -999,10 +1014,12 @@ _expectations = { test_bsddb test_curses test_dl + test_epoll test_gdbm test_gzip test_largefile test_locale + test_kqueue test_minidom test_openpty test_pyexpat @@ -1015,8 +1032,10 @@ _expectations = { test_curses test_dl test_gdbm + test_epoll test_largefile test_locale + test_kqueue test_mhlib test_mmap test_poll @@ -1027,7 +1046,9 @@ _expectations = { test_bsddb3 test_curses test_dbm + test_epoll test_ioctl + test_kqueue test_largefile test_locale test_ossaudiodev @@ -1040,6 +1061,8 @@ _expectations = { test_commands test_curses test_dl + test_epoll + test_kqueue test_largefile test_mhlib test_mmap @@ -1053,6 +1076,7 @@ _expectations = { """ test_bsddb test_bsddb3 + test_epoll test_gdbm test_locale test_ossaudiodev @@ -1069,8 +1093,10 @@ _expectations = { test_bsddb3 test_bz2 test_dl + test_epoll test_gdbm test_gzip + test_kqueue test_ossaudiodev test_tcl test_zipimport @@ -1082,6 +1108,7 @@ _expectations = { test_bsddb3 test_ctypes test_dl + test_epoll test_gdbm test_locale test_normalization @@ -1096,6 +1123,7 @@ _expectations = { test_ctypes test_curses test_dl + test_epoll test_gdbm test_locale test_ossaudiodev diff --git a/Lib/test/test_epoll.py b/Lib/test/test_epoll.py new file mode 100644 index 0000000..877a54e --- /dev/null +++ b/Lib/test/test_epoll.py @@ -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 as 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 as 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 as 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(b"Hello!") + server.send(b"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() diff --git a/Lib/test/test_kqueue.py b/Lib/test/test_kqueue.py new file mode 100644 index 0000000..310eb33 --- /dev/null +++ b/Lib/test/test_kqueue.py @@ -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 as 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(b"Hello!") + server.send(b"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() diff --git a/Lib/test/test_poll.py b/Lib/test/test_poll.py index cd9bfb0..d546c78 100644 --- a/Lib/test/test_poll.py +++ b/Lib/test/test_poll.py @@ -34,7 +34,8 @@ class PollTests(unittest.TestCase): 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) diff --git a/Lib/test/test_signal.py b/Lib/test/test_signal.py index 1d25814..2834076 100644 --- a/Lib/test/test_signal.py +++ b/Lib/test/test_signal.py @@ -1,6 +1,11 @@ import unittest from test import test_support +from contextlib import closing, nested +import pickle +import select import signal +import subprocess +import traceback import sys, os, time, errno if sys.platform[:3] in ('win', 'os2') or sys.platform == 'riscos': @@ -11,40 +16,20 @@ if sys.platform[:3] in ('win', 'os2') or sys.platform == 'riscos': class HandlerBCalled(Exception): pass + +def exit_subprocess(): + """Use os._exit(0) to exit the current subprocess. + + Otherwise, the test catches the SystemExit and continues executing + in parallel with the original test, so you wind up with an + exponential number of tests running concurrently. + """ + os._exit(0) + + class InterProcessSignalTests(unittest.TestCase): MAX_DURATION = 20 # Entire test should last at most 20 sec. - # Set up a child to send signals to us (the parent) after waiting - # long enough to receive the alarm. It seems we miss the alarm - # for some reason. This will hopefully stop the hangs on - # Tru64/Alpha. Alas, it doesn't. Tru64 appears to miss all the - # signals at times, or seemingly random subsets of them, and - # nothing done in force_test_exit so far has actually helped. - def spawn_force_test_exit_process(self, parent_pid): - # Sigh, both imports seem necessary to avoid errors. - import os - fork_pid = os.fork() - if fork_pid: - # In parent. - return fork_pid - - # In child. - import os, time - try: - # Wait 5 seconds longer than the expected alarm to give enough - # time for the normal sequence of events to occur. This is - # just a stop-gap to try to prevent the test from hanging. - time.sleep(self.MAX_DURATION + 5) - print(" child should not have to kill parent", - file=sys.__stdout__) - for signame in "SIGHUP", "SIGUSR1", "SIGUSR2", "SIGALRM": - os.kill(parent_pid, getattr(signal, signame)) - print(" child sent", signame, "to", - parent_pid, file=sys.__stdout__) - time.sleep(1) - finally: - os._exit(0) - def handlerA(self, *args): self.a_called = True if test_support.verbose: @@ -56,121 +41,133 @@ class InterProcessSignalTests(unittest.TestCase): print("handlerB invoked", args) raise HandlerBCalled(*args) - def test_main(self): - self.assertEquals(signal.getsignal(signal.SIGHUP), self.handlerA) - self.assertEquals(signal.getsignal(signal.SIGUSR1), self.handlerB) - self.assertEquals(signal.getsignal(signal.SIGUSR2), signal.SIG_IGN) - self.assertEquals(signal.getsignal(signal.SIGALRM), - signal.default_int_handler) - - # Launch an external script to send us signals. - # We expect the external script to: - # send HUP, which invokes handlerA to set a_called - # send USR1, which invokes handlerB to set b_called and raise - # HandlerBCalled - # send USR2, which is ignored - # - # Then we expect the alarm to go off, and its handler raises - # KeyboardInterrupt, finally getting us out of the loop. + def wait(self, child): + """Wait for child to finish, ignoring EINTR.""" + while True: + try: + child.wait() + return + except OSError as e: + if e.errno != errno.EINTR: + raise - if test_support.verbose: - verboseflag = '-x' - else: - verboseflag = '+x' + def run_test(self): + # Install handlers. This function runs in a sub-process, so we + # don't worry about re-setting the default handlers. + signal.signal(signal.SIGHUP, self.handlerA) + signal.signal(signal.SIGUSR1, self.handlerB) + signal.signal(signal.SIGUSR2, signal.SIG_IGN) + signal.signal(signal.SIGALRM, signal.default_int_handler) + + # Variables the signals will modify: + self.a_called = False + self.b_called = False - pid = self.pid + # Let the sub-processes know who to send signals to. + pid = os.getpid() if test_support.verbose: print("test runner's pid is", pid) - # Shell script that will send us asynchronous signals - script = """ - ( - set %(verboseflag)s - sleep 2 - kill -HUP %(pid)d - sleep 2 - kill -USR1 %(pid)d - sleep 2 - kill -USR2 %(pid)d - ) & - """ % vars() - - signal.alarm(self.MAX_DURATION) - - handler_b_exception_raised = False + child = subprocess.Popen(['kill', '-HUP', str(pid)]) + self.wait(child) + self.assertTrue(self.a_called) + self.assertFalse(self.b_called) + self.a_called = False - os.system(script) try: + child = subprocess.Popen(['kill', '-USR1', str(pid)]) + # This wait should be interrupted by the signal's exception. + self.wait(child) + self.fail('HandlerBCalled exception not thrown') + except HandlerBCalled: + self.assertTrue(self.b_called) + self.assertFalse(self.a_called) if test_support.verbose: - print("starting pause() loop...") - while 1: - try: - if test_support.verbose: - print("call pause()...") - signal.pause() - if test_support.verbose: - print("pause() returned") - except HandlerBCalled: - handler_b_exception_raised = True - if test_support.verbose: - print("HandlerBCalled exception caught") + print("HandlerBCalled exception caught") + child = subprocess.Popen(['kill', '-USR2', str(pid)]) + self.wait(child) # Nothing should happen. + + try: + signal.alarm(1) + # The race condition in pause doesn't matter in this case, + # since alarm is going to raise a KeyboardException, which + # will skip the call. + signal.pause() except KeyboardInterrupt: if test_support.verbose: print("KeyboardInterrupt (the alarm() went off)") - - self.assert_(self.a_called) - self.assert_(self.b_called) - self.assert_(handler_b_exception_raised) - - def setUp(self): - # Install handlers. - self.hup = signal.signal(signal.SIGHUP, self.handlerA) - self.usr1 = signal.signal(signal.SIGUSR1, self.handlerB) - self.usr2 = signal.signal(signal.SIGUSR2, signal.SIG_IGN) - self.alrm = signal.signal(signal.SIGALRM, - signal.default_int_handler) - self.a_called = False - self.b_called = False - self.pid = os.getpid() - self.fork_pid = self.spawn_force_test_exit_process(self.pid) - - def tearDown(self): - # Forcibly kill the child we created to ping us if there was a - # test error. - try: - # Make sure we don't kill ourself if there was a fork - # error. - if self.fork_pid > 0: - os.kill(self.fork_pid, signal.SIGKILL) except: - # If the child killed us, it has probably exited. Killing - # a non-existent process will raise an error which we - # don't care about. - pass - - # Restore handlers. - signal.alarm(0) # cancel alarm in case we died early - signal.signal(signal.SIGHUP, self.hup) - signal.signal(signal.SIGUSR1, self.usr1) - signal.signal(signal.SIGUSR2, self.usr2) - signal.signal(signal.SIGALRM, self.alrm) + self.fail('Some other exception woke us from pause: %s' % + traceback.format_exc()) + else: + self.fail('pause returned of its own accord') + + def test_main(self): + # This function spawns a child process to insulate the main + # test-running process from all the signals. It then + # communicates with that child process over a pipe and + # re-raises information about any exceptions the child + # throws. The real work happens in self.run_test(). + os_done_r, os_done_w = os.pipe() + with nested(closing(os.fdopen(os_done_r, 'rb')), + closing(os.fdopen(os_done_w, 'wb'))) as (done_r, done_w): + child = os.fork() + if child == 0: + # In the child process; run the test and report results + # through the pipe. + try: + done_r.close() + # Have to close done_w again here because + # exit_subprocess() will skip the enclosing with block. + with closing(done_w): + try: + self.run_test() + except: + pickle.dump(traceback.format_exc(), done_w) + else: + pickle.dump(None, done_w) + except: + print('Uh oh, raised from pickle.') + traceback.print_exc() + finally: + exit_subprocess() + + done_w.close() + # Block for up to MAX_DURATION seconds for the test to finish. + r, w, x = select.select([done_r], [], [], self.MAX_DURATION) + if done_r in r: + tb = pickle.load(done_r) + if tb: + self.fail(tb) + else: + os.kill(child, signal.SIGKILL) + self.fail('Test deadlocked after %d seconds.' % + self.MAX_DURATION) class BasicSignalTests(unittest.TestCase): + def trivial_signal_handler(self, *args): + pass + def test_out_of_range_signal_number_raises_error(self): self.assertRaises(ValueError, signal.getsignal, 4242) - def trivial_signal_handler(*args): - pass - self.assertRaises(ValueError, signal.signal, 4242, - trivial_signal_handler) + self.trivial_signal_handler) def test_setting_signal_handler_to_none_raises_error(self): self.assertRaises(TypeError, signal.signal, signal.SIGUSR1, None) + def test_getsignal(self): + hup = signal.signal(signal.SIGHUP, self.trivial_signal_handler) + self.assertEquals(signal.getsignal(signal.SIGHUP), + self.trivial_signal_handler) + signal.signal(signal.SIGHUP, hup) + self.assertEquals(signal.getsignal(signal.SIGHUP), hup) + + class WakeupSignalTests(unittest.TestCase): TIMEOUT_FULL = 10 TIMEOUT_HALF = 5 @@ -233,7 +230,7 @@ class SiginterruptTest(unittest.TestCase): os.kill(ppid, self.signum) time.sleep(0.2) finally: - os._exit(0) + exit_subprocess() try: os.close(w) diff --git a/Lib/test/test_threading.py b/Lib/test/test_threading.py index 3d5c120..67d9ed9 100644 --- a/Lib/test/test_threading.py +++ b/Lib/test/test_threading.py @@ -30,32 +30,26 @@ class TestThread(threading.Thread): self.nrunning = nrunning def run(self): - delay = random.random() * 2 + delay = random.random() / 10000.0 if verbose: print('task', self.getName(), 'will run for', delay, 'sec') - self.sema.acquire() + with self.sema: + with self.mutex: + self.nrunning.inc() + if verbose: + print(self.nrunning.get(), 'tasks are running') + self.testcase.assert_(self.nrunning.get() <= 3) - self.mutex.acquire() - self.nrunning.inc() - if verbose: - print(self.nrunning.get(), 'tasks are running') - self.testcase.assert_(self.nrunning.get() <= 3) - self.mutex.release() - - time.sleep(delay) - if verbose: - print('task', self.getName(), 'done') - - self.mutex.acquire() - self.nrunning.dec() - self.testcase.assert_(self.nrunning.get() >= 0) - if verbose: - print(self.getName(), 'is finished.', self.nrunning.get(), \ - 'tasks are running') - self.mutex.release() - - self.sema.release() + time.sleep(delay) + if verbose: + print('task', self.getName(), 'done') + with self.mutex: + self.nrunning.dec() + self.testcase.assert_(self.nrunning.get() >= 0) + if verbose: + print('%s is finished. %d tasks are running' % + self.getName(), self.nrunning.get()) class ThreadTests(unittest.TestCase): @@ -218,6 +212,10 @@ class ThreadTests(unittest.TestCase): rc = subprocess.call([sys.executable, "-c", """if 1: import ctypes, sys, time, thread + # This lock is used as a simple event variable. + ready = thread.allocate_lock() + ready.acquire() + # Module globals are cleared before __del__ is run # So we save the functions in class dict class C: @@ -229,10 +227,11 @@ class ThreadTests(unittest.TestCase): def waitingThread(): x = C() + ready.release() time.sleep(100) thread.start_new_thread(waitingThread, ()) - time.sleep(1) # be sure the other thread is waiting + ready.acquire() # Be sure the other thread is waiting. sys.exit(42) """]) self.assertEqual(rc, 42) @@ -242,7 +241,6 @@ class ThreadTests(unittest.TestCase): # threading.enumerate() after it has been join()ed. enum = threading.enumerate old_interval = sys.getcheckinterval() - sys.setcheckinterval(1) try: for i in range(1, 1000): t = threading.Thread(target=lambda: None) diff --git a/Lib/test/test_urllib2.py b/Lib/test/test_urllib2.py index 6ceec06..9f0b2c4 100644 --- a/Lib/test/test_urllib2.py +++ b/Lib/test/test_urllib2.py @@ -906,13 +906,14 @@ class HandlerTests(unittest.TestCase): self.assertEqual([(handlers[0], "http_open")], [tup[0:2] for tup in o.calls]) - def test_basic_auth(self): + def test_basic_auth(self, quote_char='"'): opener = OpenerDirector() password_manager = MockPasswordManager() auth_handler = urllib2.HTTPBasicAuthHandler(password_manager) realm = "ACME Widget Store" http_handler = MockHTTPHandler( - 401, 'WWW-Authenticate: Basic realm="%s"\r\n\r\n' % realm) + 401, 'WWW-Authenticate: Basic realm=%s%s%s\r\n\r\n' % + (quote_char, realm, quote_char) ) opener.add_handler(auth_handler) opener.add_handler(http_handler) self._test_basic_auth(opener, auth_handler, "Authorization", @@ -921,6 +922,9 @@ class HandlerTests(unittest.TestCase): "http://acme.example.com/protected", ) + def test_basic_auth_with_single_quoted_realm(self): + self.test_basic_auth(quote_char="'") + def test_proxy_basic_auth(self): opener = OpenerDirector() ph = urllib2.ProxyHandler(dict(http="proxy.example.com:3128")) diff --git a/Lib/urllib2.py b/Lib/urllib2.py index 91c632c..b95d672 100644 --- a/Lib/urllib2.py +++ b/Lib/urllib2.py @@ -777,7 +777,10 @@ class AbstractBasicAuthHandler: # XXX this allows for multiple auth-schemes, but will stupidly pick # the last one with a realm specified. - rx = re.compile('(?:.*,)*[ \t]*([^ \t]+)[ \t]+realm="([^"]*)"', re.I) + # allow for double- and single-quoted realm values + # (single quotes are a violation of the RFC, but appear in the wild) + rx = re.compile('(?:.*,)*[ \t]*([^ \t]+)[ \t]+' + 'realm=(["\'])(.*?)\\2', re.I) # XXX could pre-emptively send auth info already accepted (RFC 2617, # end of section 2, and section 1.2 immediately after "credentials" @@ -797,7 +800,7 @@ class AbstractBasicAuthHandler: if authreq: mo = AbstractBasicAuthHandler.rx.search(authreq) if mo: - scheme, realm = mo.groups() + scheme, quote, realm = mo.groups() if scheme.lower() == 'basic': return self.retry_http_basic_auth(host, req, realm) diff --git a/Modules/selectmodule.c b/Modules/selectmodule.c index 168197b..0f0cabd 100644 --- a/Modules/selectmodule.c +++ b/Modules/selectmodule.c @@ -5,6 +5,7 @@ */ #include "Python.h" +#include <structmember.h> #ifdef __APPLE__ /* Perform runtime testing for a broken poll on OSX to make it easier @@ -53,7 +54,6 @@ extern void bzero(void *, int); # endif #endif - static PyObject *SelectError; /* list of Python objects and their file descriptor */ @@ -238,7 +238,7 @@ select_select(PyObject *self, PyObject *args) 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; } @@ -402,11 +402,59 @@ poll_register(pollObject *self, PyObject *args) 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 = PyLong_FromLong(fd); + if (key == NULL) + return NULL; + if (PyDict_GetItem(self->dict, key) == NULL) { + errno = ENOENT; + PyErr_SetFromErrno(PyExc_IOError); + return NULL; + } + value = PyLong_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."); @@ -480,9 +528,9 @@ poll_poll(pollObject *self, PyObject *args) 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); @@ -540,7 +588,9 @@ poll_poll(pollObject *self, PyObject *args) 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}, @@ -643,6 +693,1007 @@ static int select_have_broken_poll(void) #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 PyLong_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 PyLong_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\ @@ -664,14 +1715,15 @@ arguments; each contains the subset of the corresponding file descriptors\n\ 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, @@ -691,8 +1743,8 @@ initselect(void) 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) { @@ -727,4 +1779,103 @@ initselect(void) #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 */ } @@ -5390,13 +5390,15 @@ done + + 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 \ @@ -16130,7 +16132,111 @@ echo "${ECHO_T}no" >&6; } 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 diff --git a/configure.in b/configure.in index d402019..c1c25ff 100644 --- a/configure.in +++ b/configure.in @@ -1077,8 +1077,8 @@ 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 \ @@ -2325,7 +2325,21 @@ AC_TRY_COMPILE([#include <unistd.h>], void *x=fdatasync, 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 diff --git a/pyconfig.h.in b/pyconfig.h.in index 135da3f..f533de2 100644 --- a/pyconfig.h.in +++ b/pyconfig.h.in @@ -144,6 +144,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 @@ -315,6 +318,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 @@ -630,6 +636,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 |