Commits

Palmer, 2E0EOL  committed 20c78ab

Stock python-select26 0.1a3 package from upstream.

  • Participants

Comments (0)

Files changed (19)

+0.1a3
+-----
+
+ * New release based on Python trunk r62498 
+
+0.1a2
+-----
+
+ * Removed dependency on setuptools as requested by therve.
+
+0.1a1
+-----
+
+ * initial release
+Copyright (c) 2007 Christian Heimes
+
+Copyright (c) 2000 Doug White
+Copyright (c) 2006 James Knight
+
+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.
+
+include README
+include CHANGES
+include LICENSE
+include Makefile
+include runtests.py
+PYTHON?=python2.5
+TESTFLAGS=-v
+TESTOPTS=
+SETUPFLAGS=
+
+all: inplace
+
+# Build in-place
+inplace:
+	$(PYTHON) setup.py $(SETUPFLAGS) build_ext -i
+
+.PHONY=build
+build: 
+	$(PYTHON) setup.py $(SETUPFLAGS) build
+
+bdist_egg:
+	$(PYTHON) setup.py $(SETUPFLAGS) bdist_egg
+
+test_build: build 
+	$(PYTHON) runtests.py $(TESTFLAGS) $(TESTOPTS)
+
+test_inplace: inplace 
+	$(PYTHON) runtests.py $(TESTFLAGS) $(TESTOPTS)
+	
+sdist: egg
+	$(PYTHON) setup.py $(SETUPFLAGS) sdist --format=gztar
+
+upload: 
+	$(PYTHON) setup.py $(SETUPFLAGS) register sdist --format=gztar upload --sign
+
+install:
+	$(PYTHON) setup.py $(SETUPFLAGS) install
+
+# What should the default be?
+test: test_inplace
+
+egg: bdist_egg
+
+clean:
+	find . \( -name '*.o' -o -name '*~' -o -name '*.so' -o -name '*.py[cod]' -o -name '*.dll' \) -exec rm -f {} \;
+	rm -rf build
+
+realclean: clean
+	rm -f TAGS
+	rm -rf dist
+	$(PYTHON) setup.py clean -a
+
+Metadata-Version: 1.1
+Name: select26
+Version: 0.1a3
+Summary: Backport of the new select module with epoll and kqueue interface
+Home-page: UNKNOWN
+Author: Christian Heimes
+Author-email: christian@cheimes.de
+License: MIT
+Download-URL: http://pypi.python.org/
+Description: 
+        The select26 extension is a backport of the new API functions of Python
+        2.6 for Python 2.3 to 2.5. It contains object oriented wrappers for epoll
+        (Linux 2.6) and kqueue/kevent (BSD).
+        
+        >>> try:
+        ...     import select26 as select
+        ... except ImportError:
+        ...     import select
+        
+        >>> ep = select.epoll()
+        >>> kq = select.kqueue()
+        
+        This release is based upon Python svn trunk r62498.
+        
+Keywords: select poll epoll kqueue
+Platform: Linux 2.6
+Platform: BSD
+Platform: Mac OS X
+Classifier: Development Status :: 3 - Alpha
+Classifier: Intended Audience :: Developers
+Classifier: License :: OSI Approved :: MIT License
+Classifier: Natural Language :: English
+Classifier: Programming Language :: C
+Classifier: Programming Language :: Python
+Classifier: Operating System :: MacOS :: MacOS X
+Classifier: Operating System :: POSIX :: BSD
+Classifier: Operating System :: POSIX :: Linux
+Classifier: Topic :: System :: Networking
+Provides: select26
+The select26 module is a backport of my patch for Python 2.6 to Python 2.4
+and 2.5. It contains an interface to Linux's epoll() and BSD's kqueue
+system calls.
+
+>>> try:
+...     import select26 as select
+... except ImportError:
+...     import select
+>>> ep = select.epoll()
+
+#!/usr/bin/env python
+#
+# SchoolTool - common information systems platform for school administration
+# Copyright (c) 2003 Shuttleworth Foundation
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+#
+"""
+SchoolTool test runner.
+
+Syntax: test.py [options] [pathname-regexp [test-regexp]]
+
+There are two kinds of tests:
+  - unit tests (or programmer tests) test the internal workings of various
+    components of the system
+  - functional tests (acceptance tests, customer tests) test only externaly
+    visible system behaviour
+
+You can choose to run unit tests (this is the default mode), functional tests
+(by giving a -f option to test.py) or both (by giving both -u and -f options).
+
+Test cases are located in the directory tree starting at the location of this
+script, in subdirectories named 'tests' for unit tests and 'ftests' for
+functional tests, in Python modules named 'test*.py'.  They are then filtered
+according to pathname and test regexes.  Alternatively, packages may just have
+'tests.py' and 'ftests.py' instead of subpackages 'tests' and 'ftests'
+respectively.
+
+A leading "!" in a regexp is stripped and negates the regexp.  Pathname
+regexp is applied to the whole path (package/package/module.py). Test regexp
+is applied to a full test id (package.package.module.class.test_method).
+
+Options:
+  -h, --help            print this help message
+  -v                    verbose (print dots for each test run)
+  -vv                   very verbose (print test names)
+  -q                    quiet (do not print anything on success)
+  -c                    colorize output (assumes dark background)
+  -C                    colorize output (assumes bright background)
+  -w                    enable warnings about omitted test cases
+  -d                    invoke pdb when an exception occurs
+  -1                    report only the first failure in doctests
+  -p                    show progress bar (can be combined with -v or -vv)
+  -u                    select unit tests (default)
+  -f                    select functional tests
+  --level n             select only tests at level n or lower
+  --all-levels          select all tests
+  --list-files          list all selected test files
+  --list-tests          list all selected test cases
+  --list-hooks          list all loaded test hooks
+  --coverage            create code coverage reports
+  --search-in dir       limit directory tree walk to dir (optimisation)
+  --immediate-errors    show errors as soon as they happen (default)
+  --delayed-errors      show errors after all unit tests were run
+"""
+#
+# This script borrows ideas from Zope 3's test runner heavily.  It is smaller
+# and cleaner though, at the expense of more limited functionality.
+#
+
+import re
+import os
+import sys
+import time
+import types
+import getopt
+import doctest
+import unittest
+import traceback
+import linecache
+import pdb
+from sets import Set
+
+__metaclass__ = type
+
+
+class Options:
+    """Configurable properties of the test runner."""
+
+    # test location
+    basedir = '.'                # base directory for tests (defaults to
+                                # basedir of argv[0] + 'src'), must be absolute
+    search_in = ()              # list of subdirs to traverse (defaults to
+                                # basedir)
+    follow_symlinks = True      # should symlinks to subdirectories be
+                                # followed? (hardcoded, may cause loops)
+
+    # which tests to run
+    unit_tests = False          # unit tests (default if both are false)
+    functional_tests = False    # functional tests
+
+    # test filtering
+    level = 1                   # run only tests at this or lower level
+                                # (if None, runs all tests)
+    pathname_regex = ''         # regexp for filtering filenames
+    test_regex = ''             # regexp for filtering test cases
+
+    # actions to take
+    list_files = False          # --list-files
+    list_tests = False          # --list-tests
+    list_hooks = False          # --list-hooks
+    run_tests = True            # run tests (disabled by --list-foo)
+    postmortem = False          # invoke pdb when an exception occurs
+
+    # output verbosity
+    verbosity = 0               # verbosity level (-v)
+    quiet = 0                   # do not print anything on success (-q)
+    warn_omitted = False        # produce warnings when a test case is
+                                # not included in a test suite (-w)
+    first_doctest_failure = False # report first doctest failure (-1)
+    print_import_time = True    # print time taken to import test modules
+                                # (currently hardcoded)
+    progress = False            # show running progress (-p)
+    colorizer = None            # colorize output (-c)
+    coverage = False            # produce coverage reports (--coverage)
+    coverdir = 'coverage'       # where to put them (currently hardcoded)
+    immediate_errors = True     # show tracebacks twice (--immediate-errors,
+                                # --delayed-errors)
+    screen_width = 80           # screen width (autodetected)
+
+
+def compile_matcher(regex):
+    """Return a function that takes one argument and returns True or False.
+
+    Regex is a regular expression.  Empty regex matches everything.  There
+    is one expression: if the regex starts with "!", the meaning of it is
+    reversed.
+    """
+    if not regex:
+        return lambda x: True
+    elif regex == '!':
+        return lambda x: False
+    elif regex.startswith('!'):
+        rx = re.compile(regex[1:])
+        return lambda x: rx.search(x) is None
+    else:
+        rx = re.compile(regex)
+        return lambda x: rx.search(x) is not None
+
+
+def walk_with_symlinks(top, func, arg):
+    """Like os.path.walk, but follows symlinks on POSIX systems.
+
+    If the symlinks create a loop, this function will never finish.
+    """
+    try:
+        names = os.listdir(top)
+    except os.error:
+        return
+    func(arg, top, names)
+    exceptions = ('.', '..')
+    for name in names:
+        if name not in exceptions:
+            name = os.path.join(top, name)
+            if os.path.isdir(name):
+                walk_with_symlinks(name, func, arg)
+
+
+def get_test_files(cfg):
+    """Return a list of test module filenames."""
+    matcher = compile_matcher(cfg.pathname_regex)
+    allresults = []
+    testdir_names = []
+    if cfg.functional_tests:
+        testdir_names.append('ftests')
+    if cfg.unit_tests:
+        testdir_names.append('tests')
+    baselen = len(cfg.basedir) + 1
+    def visit(ignored, dir, files):
+        # Ignore files starting with a dot.
+        # Do not not descend into subdirs containing a dot.
+        remove = []
+        for idx, file in enumerate(files):
+            if file.startswith('.'):
+                remove.append(idx)
+            elif '.' in file and os.path.isdir(os.path.join(dir, file)):
+                remove.append(idx)
+        remove.reverse()
+        for idx in remove:
+            del files[idx]
+        # Skip non-test directories, but look for tests.py and/or ftests.py
+        if os.path.basename(dir) != testdir_name:
+            if testdir_name + '.py' in files:
+                path = os.path.join(dir, testdir_name + '.py')
+                if matcher(path[baselen:]):
+                    results.append(path)
+            return
+        if '__init__.py' not in files:
+            print >> sys.stderr, "%s is not a package" % dir
+            return
+        for file in files:
+            if file.startswith('test') and file.endswith('.py'):
+                path = os.path.join(dir, file)
+                if matcher(path[baselen:]):
+                    results.append(path)
+    if cfg.follow_symlinks:
+        walker = walk_with_symlinks
+    else:
+        walker = os.path.walk
+
+    for testdir_name in testdir_names:
+        results = []
+        for dir in cfg.search_in:
+            walker(dir, visit, None)
+        results.sort()
+        allresults += results
+
+    return allresults
+
+
+def import_module(filename, cfg, tracer=None):
+    """Import and return a module."""
+    filename = os.path.splitext(filename)[0]
+    if filename.startswith(cfg.basedir):
+        filename = filename[len(cfg.basedir):]
+    modname = filename.replace(os.path.sep, '.')
+    if modname.startswith('.'):
+        modname = modname[1:]
+    if tracer is not None:
+        mod = tracer.runfunc(__import__, modname)
+    else:
+        mod = __import__(modname)
+    components = modname.split('.')
+    for comp in components[1:]:
+        mod = getattr(mod, comp)
+    return mod
+
+
+# Classess passed to isinstance to see whether a test is a DocFileCase.
+# There's doctest.DocFileCase (if you have Python 2.4), and then there might be
+# zope.testing.doctest.DocFileCase.
+if hasattr(doctest, 'DocFileCase'):
+    DocFileCase_classes = (doctest.DocFileCase,)
+else:
+    DocFileCase_classes = ()
+
+
+def name_of_test(test, basedir=None):
+    """Return the name of a test.
+
+    In most cases the name will be "package.module.class.method", however it
+    is different for doctest files, where it will be "subdir/subdir/filename".
+    """
+    if isinstance(test, DocFileCase_classes):
+        # test.id() returns something like "README_txt", while str(test)
+        # returns the pathname
+        doctest_filename = os.path.abspath(str(test))
+        if basedir and doctest_filename.startswith(basedir + '/'):
+            doctest_filename = doctest_filename[len(basedir) + 1:]
+        return doctest_filename
+    else:
+        # test.id() returns something like
+        # "package.module.TestClass.test_method", while str(test)
+        # returns "test_method (package.module.TestClass)".
+        return test.id()
+
+
+def filter_testsuite(suite, matcher, level=None, basedir=None):
+    """Return a flattened list of test cases that match the given matcher."""
+    if not isinstance(suite, unittest.TestSuite):
+        raise TypeError('not a TestSuite', suite)
+    results = []
+    for test in suite._tests:
+        if level is not None and getattr(test, 'level', 0) > level:
+            continue
+        if isinstance(test, unittest.TestCase):
+            testname = name_of_test(test, basedir)
+            if matcher(testname):
+                results.append(test)
+        else:
+            filtered = filter_testsuite(test, matcher, level, basedir)
+            results.extend(filtered)
+    return results
+
+
+def get_all_test_cases(module):
+    """Return a list of all test case classes defined in a given module."""
+    results = []
+    for name in dir(module):
+        if not name.startswith('Test'):
+            continue
+        item = getattr(module, name)
+        if (isinstance(item, (type, types.ClassType)) and
+            issubclass(item, unittest.TestCase)):
+            results.append(item)
+    return results
+
+
+def get_test_classes_from_testsuite(suite):
+    """Return a set of test case classes used in a test suite."""
+    if not isinstance(suite, unittest.TestSuite):
+        raise TypeError('not a TestSuite', suite)
+    results = Set()
+    for test in suite._tests:
+        if isinstance(test, unittest.TestCase):
+            results.add(test.__class__)
+        else:
+            classes = get_test_classes_from_testsuite(test)
+            results.update(classes)
+    return results
+
+
+def get_test_cases(test_files, cfg, tracer=None):
+    """Return a list of test cases from a given list of test modules."""
+    matcher = compile_matcher(cfg.test_regex)
+    results = []
+    startTime = time.time()
+    for file in test_files:
+        module = import_module(file, cfg, tracer=tracer)
+        try:
+            func = module.test_suite
+        except AttributeError:
+            print >> sys.stderr
+            print >> sys.stderr, ("%s: WARNING: there is no test_suite"
+                                  " function" % file)
+            print >> sys.stderr
+            continue
+        if tracer is not None:
+            test_suite = tracer.runfunc(func)
+        else:
+            test_suite = func()
+        if test_suite is None:
+            continue
+        if cfg.warn_omitted:
+            all_classes = Set(get_all_test_cases(module))
+            classes_in_suite = get_test_classes_from_testsuite(test_suite)
+            difference = all_classes - classes_in_suite
+            for test_class in difference:
+                # surround the warning with blank lines, otherwise it tends
+                # to get lost in the noise
+                print >> sys.stderr
+                print >> sys.stderr, ("%s: WARNING: %s not in test suite"
+                                      % (file, test_class.__name__))
+                print >> sys.stderr
+        if (cfg.level is not None and
+            getattr(test_suite, 'level', 0) > cfg.level):
+            continue
+        filtered = filter_testsuite(test_suite, matcher, cfg.level, cfg.basedir)
+        results.extend(filtered)
+    stopTime = time.time()
+    timeTaken = float(stopTime - startTime)
+    if cfg.print_import_time:
+        nmodules = len(test_files)
+        plural = (nmodules != 1) and 's' or ''
+        print "Imported %d module%s in %.3fs" % (nmodules, plural, timeTaken)
+        print
+    return results
+
+
+def get_test_hooks(test_files, cfg, tracer=None):
+    """Return a list of test hooks from a given list of test modules."""
+    dirs = Set(map(os.path.dirname, test_files))
+    for dir in list(dirs):
+        if os.path.basename(dir) == 'ftests':
+            dirs.add(os.path.join(os.path.dirname(dir), 'tests'))
+    dirs = list(dirs)
+    dirs.sort()
+    hook_modules = []
+    for dir in dirs:
+        filename = os.path.join(dir, 'checks.py')
+        if os.path.exists(filename):
+            module = import_module(filename, cfg, tracer=tracer)
+            hook_modules.append(module)
+    # Also look in a a directory 'testsupport' which is a sibling of
+    # cfg.basedir
+    dir = os.path.join(os.path.dirname(cfg.basedir), 'testsupport')
+    filename = os.path.join(dir, 'checks.py')
+    if os.path.exists(filename):
+        sys.path.insert(0, dir)
+        try:
+            module = import_module('checks.py', cfg, tracer=tracer)
+            hook_modules.append(module)
+        finally:
+            del sys.path[0]
+    results = []
+    for module in hook_modules:
+        if tracer is not None:
+            hooks = tracer.runfunc(module.test_hooks)
+        else:
+            hooks = module.test_hooks()
+        results.extend(hooks)
+    return results
+
+
+def extract_tb(tb, limit=None):
+    """Improved version of traceback.extract_tb.
+
+    Includes a dict with locals in every stack frame instead of the line.
+    """
+    list = []
+    while tb is not None and (limit is None or len(list) < limit):
+        frame = tb.tb_frame
+        code = frame.f_code
+        name = code.co_name
+        filename = code.co_filename
+        lineno = tb.tb_lineno
+        locals = frame.f_locals
+        list.append((filename, lineno, name, locals))
+        tb = tb.tb_next
+    return list
+
+
+
+colorcodes = {'gray': 0, 'red': 1, 'green': 2, 'yellow': 3,
+              'blue': 4, 'magenta': 5, 'cyan': 6, 'white': 7}
+
+dark_colormap = { # Color scheme for dark backgrounds
+            'fail': 'red',
+            'pass': 'green',
+            'count': 'white',
+            'title': 'white',
+            'separator': 'dark white',
+            'longtestname': 'yellow',
+            'filename': 'dark green',
+            'lineno': 'green',
+            'testname': 'dark yellow',
+            'excname': 'red',
+            'excstring': 'yellow',
+            'tbheader': 'dark white',
+            'doctest_ignored': 'gray',
+            'doctest_title': 'dark white',
+            'doctest_code': 'yellow',
+            'doctest_expected': 'green',
+            'doctest_got': 'red',
+            'diff_expected': 'red',
+            'diff_actual': 'green',
+            'diff_context': 'dark white',
+            'diff_inline': 'gray'}
+
+light_colormap = { # Color scheme for light backgrounds
+            'fail': 'red',
+            'pass': 'dark green',
+            'count': 'dark green',
+            'title': 'red',
+            'separator': 'dark white',
+            'longtestname': 'dark red',
+            'filename': 'dark green',
+            'lineno': 'dark magenta',
+            'testname': 'dark yellow',
+            'excname': 'red',
+            'excstring': 'dark yellow',
+            'tbheader': 'gray',
+            'doctest_ignored': 'dark white',
+            'doctest_title': 'gray',
+            'doctest_code': 'dark blue',
+            'doctest_expected': 'dark green',
+            'doctest_got': 'dark red',
+            'diff_expected': 'dark red',
+            'diff_actual': 'dark green',
+            'diff_context': 'dark gray',
+            'diff_inline': 'dark magenta'}
+
+
+class Colorizer(object):
+
+    def __init__(self, colormap):
+        self.colormap = colormap
+
+    def colorize(self, texttype, text):
+        """Colorize text by ANSI escape codes in a color provided in colormap."""
+        color = self.colormap[texttype]
+        if color.startswith('dark '):
+            light = 0
+            color = color[len('dark '):] # strip the 'dark' prefix
+        else:
+            light = 1
+        code = 30 + colorcodes[color]
+        return '\033[%d;%dm' % (light, code) + text + '\033[0m'
+
+    def colorize_ndiff(self, lines):
+        """Colorize ndiff output.
+
+        Returns a new sequence of colored strings.
+
+        `lines` is a sequence of strings.
+
+        Typical input:
+
+              Some context lines
+            - This line was removed
+              Some context
+            + This line was added
+              Some context
+            - This line esd chnged
+            ?           ^^^     -
+            + This line was changd
+            ?           ^^^   +
+              Some context
+
+        """
+        result = []
+        for line in lines:
+            if line.startswith('    -'):
+                result.append(self.colorize('diff_expected', line))
+            elif line.startswith('    +'):
+                result.append(self.colorize('diff_actual', line))
+            elif line.startswith('    ?'):
+                result.append(self.colorize('diff_inline', line))
+            else:
+                result.append(self.colorize('diff_context', line))
+        return result
+
+    def colorize_zope_doctest_output(self, lines):
+        """Colorize output formatted by the doctest engine included with Zope 3.
+
+        Returns a new sequence of colored strings.
+
+        `lines` is a sequence of strings.
+
+        The typical structure of the doctest output looks either like this:
+
+            File "...", line 123, in foo.bar.baz.doctest_quux
+            Failed example:
+                f(2, 3)
+            Expected:
+                6
+            Got:
+                5
+
+        Or, if an exception has occurred, like this:
+
+            File "...", line 123, in foo.bar.baz.doctest_quux
+            Failed example:
+                f(2, 3)
+            Exception raised:
+                Traceback (most recent call last):
+                  File "...", line 123, in __init__
+                    self.do_something(a, b, c)
+                  File "...", line ...
+                    ...
+                FooError: something bad happened
+
+        If some assumption made by this function is not met, the original sequence
+        is returned without any modifications.
+        """
+        # XXX bug: doctest may report several failures in one test, they are
+        #          separated by a horizontal dash line.  Only the first one of
+        #          them is now colorized properly.
+        header = lines[0]
+        if not header.startswith('File "'):
+            return lines # not a doctest failure report?
+
+        # Dissect the header in a rather nasty way.
+        header = header[len('File "'):]
+        fn_end = header.find('"')
+        if fn_end == -1:
+            return lines
+        filename = header[:fn_end]
+        header = header[fn_end+len('", line '):]
+        parts = header.split(', in ')
+        if len(parts) != 2:
+            lineno = header
+            filename = self.colorize('filename', filename)
+            lineno = self.colorize('lineno', lineno)
+            result = ['File "%s", line %s' % (filename, lineno)]
+        else:
+            lineno, testname = parts
+            filename = self.colorize('filename', filename)
+            lineno = self.colorize('lineno', lineno)
+            testname = self.colorize('testname', testname)
+            result = ['File "%s", line %s, in %s' % (filename, lineno, testname)]
+
+        # Colorize the 'Failed example:' section.
+        if lines[1] != 'Failed example:':
+            return lines
+        result.append(self.colorize('doctest_title', lines[1]))
+        remaining = lines[2:]
+        terminators = ['Expected:', 'Expected nothing', 'Exception raised:',
+                       'Differences (ndiff with -expected +actual):']
+        while remaining and remaining[0] not in terminators:
+            line = remaining.pop(0)
+            result.append(self.colorize('doctest_code', line))
+        if not remaining:
+            return lines
+
+        if remaining[0] in ('Expected:', 'Expected nothing'):
+            result.append(self.colorize('doctest_title', remaining.pop(0))) # Expected:
+            while remaining and remaining[0] not in ('Got:', 'Got nothing'):
+                line = remaining.pop(0)
+                result.append(self.colorize('doctest_expected', line))
+            if not remaining or remaining[0] not in ('Got:', 'Got nothing'):
+                return lines
+            result.append(self.colorize('doctest_title', remaining.pop(0))) # Got:
+            while remaining:
+                line = remaining.pop(0)
+                result.append(self.colorize('doctest_got', line))
+        elif remaining[0] == 'Exception raised:':
+            result.append(self.colorize('doctest_title', remaining.pop(0))) # E. raised:
+            while remaining:
+                line = remaining.pop(0)
+                # TODO: Scrape and colorize the traceback.
+                result.append(self.colorize('doctest_got', line))
+        elif remaining[0] == 'Differences (ndiff with -expected +actual):':
+            result.append(self.colorize('doctest_title', remaining.pop(0))) # E. raised:
+            result.extend(self.colorize_ndiff(remaining))
+        else:
+            return lines
+
+        return result
+
+    def colorize_exception_only(self, lines):
+        """Colorize result of traceback.format_exception_only."""
+        if len(lines) > 1:
+            return lines # SyntaxError?  We won't deal with that for now.
+        lines = lines[0].splitlines()
+
+        # First, colorize the first line, which usually contains the name
+        # and the string of the exception.
+        result = []
+        doctest = 'Failed doctest test for' in lines[0]
+        # TODO: We only deal with the output from Zope 3's doctest module.
+        #       A colorizer for the Python's doctest module would be nice too.
+        if doctest:
+            # If we have a doctest, we do not care about this header.  All the
+            # interesting things are below, formatted by the doctest runner.
+            for lineno in range(4):
+                result.append(self.colorize('doctest_ignored', lines[lineno]))
+            beef = self.colorize_zope_doctest_output(lines[4:])
+            result.extend(beef)
+            return '\n'.join(result)
+        else:
+            # A simple exception.  Try to colorize the first row, leave others be.
+            excline = lines[0].split(': ', 1)
+            if len(excline) == 2:
+                excname = self.colorize('excname', excline[0])
+                excstring = self.colorize('excstring', excline[1])
+                result.append('%s: %s' % (excname, excstring))
+            else:
+                result.append(self.colorize('excstring', lines[0]))
+            result.extend(lines[1:])
+            return '\n'.join(result)
+
+
+def format_exception(etype, value, tb, limit=None, basedir=None,
+                     colorizer=None):
+    """Improved version of traceback.format_exception.
+
+    Includes Zope-specific extra information in tracebacks.
+
+    If colorizer is not None, it is used to colorize the output.
+    """
+    color = (colorizer is not None)
+    if color:
+        colorize = colorizer.colorize
+    # Show stack trace.
+    list = []
+    if tb:
+        list = ['Traceback (most recent call last):\n']
+        if color:
+            list[0] = colorize('tbheader', list[0])
+        w = list.append
+
+        for filename, lineno, name, locals in extract_tb(tb, limit):
+            line = linecache.getline(filename, lineno)
+            if color and 'zope/testing/doctest.py' not in filename:
+                filename = colorize('filename', filename)
+                lineno = colorize('lineno', str(lineno))
+                name = colorize('testname', name)
+                w('  File "%s", line %s, in %s\n' % (filename, lineno, name))
+                if line:
+                    w('    %s\n' % line.strip())
+            elif color:
+                s = '  File "%s", line %s, in %s\n' % (filename, lineno, name)
+                w(colorize('doctest_ignored', s))
+                if line:
+                    w('    %s\n' % colorize('doctest_ignored', line.strip()))
+            else:
+                w('  File "%s", line %s, in %s\n' % (filename, lineno, name))
+                if line:
+                    w('    %s\n' % line.strip())
+
+            tb_info = locals.get('__traceback_info__')
+            if tb_info is not None:
+                w('  Extra information: %s\n' % repr(tb_info))
+            tb_supplement = locals.get('__traceback_supplement__')
+            if tb_supplement is not None:
+                tb_supplement = tb_supplement[0](*tb_supplement[1:])
+                # TODO these should be hookable
+                from zope.tales.tales import TALESTracebackSupplement
+                from zope.pagetemplate.pagetemplate \
+                        import PageTemplateTracebackSupplement
+                if isinstance(tb_supplement, PageTemplateTracebackSupplement):
+                    template = tb_supplement.manageable_object.pt_source_file()
+                    if template:
+                        w('  Template "%s"\n' % template)
+                elif isinstance(tb_supplement, TALESTracebackSupplement):
+                    w('  Template "%s", line %s, column %s\n'
+                      % (tb_supplement.source_url, tb_supplement.line,
+                         tb_supplement.column))
+                    line = linecache.getline(tb_supplement.source_url,
+                                             tb_supplement.line)
+                    if line:
+                        w('    %s\n' % line.strip())
+                    w('  Expression: %s\n' % tb_supplement.expression)
+                else:
+                    w('  __traceback_supplement__ = %r\n' % (tb_supplement, ))
+
+    # Add the representation of the exception itself.
+    lines = traceback.format_exception_only(etype, value)
+    if color:
+        lines = colorizer.colorize_exception_only(lines)
+    list.extend(lines)
+
+    return list
+
+
+class CustomTestResult(unittest._TextTestResult):
+    """Customised TestResult.
+
+    It can show a progress bar, and displays tracebacks for errors and failures
+    as soon as they happen, in addition to listing them all at the end.
+    """
+
+    __super = unittest._TextTestResult
+    __super_init = __super.__init__
+    __super_startTest = __super.startTest
+    __super_stopTest = __super.stopTest
+    __super_printErrors = __super.printErrors
+    __super_printErrorList = __super.printErrorList
+
+    def __init__(self, stream, descriptions, verbosity, count, cfg, hooks):
+        self.__super_init(stream, descriptions, verbosity)
+        self.count = count
+        self.cfg = cfg
+        self.hooks = hooks
+        if cfg.progress:
+            self.dots = False
+            self._lastWidth = 0
+            self._maxWidth = cfg.screen_width - len("xxxx/xxxx (xxx.x%): ") - 1
+
+    def startTest(self, test):
+        n = self.testsRun + 1
+        if self.cfg.progress:
+            # verbosity == 0: 'xxxx/xxxx (xxx.x%)'
+            # verbosity == 1: 'xxxx/xxxx (xxx.x%): test name'
+            # verbosity >= 2: 'xxxx/xxxx (xxx.x%): test name ... ok'
+            self.stream.write("\r%4d" % n)
+            if self.count:
+                self.stream.write("/%d (%5.1f%%)"
+                                  % (self.count, n * 100.0 / self.count))
+            if self.showAll: # self.cfg.verbosity == 1
+                self.stream.write(": ")
+            elif self.cfg.verbosity:
+                name = self.getShortDescription(test)
+                width = len(name)
+                if width < self._lastWidth:
+                    name += " " * (self._lastWidth - width)
+                self.stream.write(": %s" % name)
+                self._lastWidth = width
+            self.stream.flush()
+        self.__super_startTest(test)  # increments testsRun by one and prints
+        self.testsRun = n # override the testsRun calculation
+        for hook in self.hooks:
+            hook.startTest(test)
+
+    def stopTest(self, test):
+        for hook in self.hooks:
+            hook.stopTest(test)
+        self.__super_stopTest(test)
+
+    def getDescription(self, test):
+        return name_of_test(test, self.cfg.basedir)
+
+    def getShortDescription(self, test):
+        s = name_of_test(test, self.cfg.basedir)
+        if len(s) > self._maxWidth:
+            # In most cases s is "package.module.class.method".
+            # Try to keep the method name intact, and replace the middle
+            # part of "package.module.class" with an ellipsis.
+            namelen = len(s.split('.')[-1])
+            left = max(0, (self._maxWidth - namelen) / 2 - 1)
+            right = self._maxWidth - left - 3
+            s = "%s...%s" % (s[:left], s[-right:])
+        return s
+
+    def printErrors(self):
+        w = self.stream.writeln
+        if self.cfg.progress and not (self.dots or self.showAll):
+            w()
+        if self.cfg.immediate_errors and (self.errors or self.failures):
+            if self.cfg.colorizer is not None:
+                w(self.cfg.colorizer.colorize('separator', self.separator1))
+                w(self.cfg.colorizer.colorize('title', "Tests that failed"))
+                w(self.cfg.colorizer.colorize('separator', self.separator2))
+            else:
+                w(self.separator1)
+                w("Tests that failed")
+                w(self.separator2)
+        self.__super_printErrors()
+
+    def formatError(self, err):
+        return "".join(format_exception(basedir=self.cfg.basedir,
+                                        colorizer=self.cfg.colorizer, *err))
+
+    def printTraceback(self, kind, test, err):
+        w = self.stream.writeln
+        if self.cfg.colorizer is not None:
+            c = self.cfg.colorizer.colorize
+        else:
+            c = lambda texttype, text: text
+        w()
+        w(c('separator', self.separator1))
+        kind = c('fail', kind)
+        description = c('longtestname', self.getDescription(test))
+        w("%s: %s" % (kind, description))
+        w(c('separator', self.separator2))
+        w(self.formatError(err))
+        w()
+
+    def addFailure(self, test, err):
+        if self.cfg.immediate_errors:
+            self.printTraceback("FAIL", test, err)
+        if self.cfg.postmortem:
+            pdb.post_mortem(sys.exc_info()[2])
+        self.failures.append((test, self.formatError(err)))
+
+    def addError(self, test, err):
+        if self.cfg.immediate_errors:
+            self.printTraceback("ERROR", test, err)
+        if self.cfg.postmortem:
+            pdb.post_mortem(sys.exc_info()[2])
+        self.errors.append((test, self.formatError(err)))
+
+    def printErrorList(self, flavour, errors):
+        if self.cfg.immediate_errors:
+            for test, err in errors:
+                description = self.getDescription(test)
+                self.stream.writeln("%s: %s" % (flavour, description))
+        else:
+            self.__super_printErrorList(flavour, errors)
+
+
+class CustomTestRunner(unittest.TextTestRunner):
+    """Customised TestRunner.
+
+    See CustomisedTextResult for a list of extensions.
+    """
+
+    __super = unittest.TextTestRunner
+    __super_init = __super.__init__
+    __super_run = __super.run
+
+    def __init__(self, cfg, hooks=None, stream=sys.stderr, count=None):
+        self.__super_init(verbosity=cfg.verbosity, stream=stream)
+        self.cfg = cfg
+        if hooks is not None:
+            self.hooks = hooks
+        else:
+            self.hooks = []
+        self.count = count
+
+    def run(self, test):
+        """Run the given test case or test suite."""
+        if self.count is None:
+            self.count = test.countTestCases()
+        if self.cfg.colorizer is not None:
+            c = self.cfg.colorizer.colorize
+        else:
+            c = lambda texttype, text: text
+        result = self._makeResult()
+        startTime = time.time()
+        test(result)
+        stopTime = time.time()
+        timeTaken = float(stopTime - startTime)
+        result.printErrors()
+        run = result.testsRun
+        if not self.cfg.quiet:
+            self.stream.writeln(c('separator', result.separator2))
+            run_str = c('count', str(run))
+            time_str = c('count', '%.3f' % timeTaken)
+            self.stream.writeln("Ran %s test%s in %ss" %
+                                (run_str, run != 1 and "s" or "", time_str))
+            self.stream.writeln()
+        if not result.wasSuccessful():
+            self.stream.write(c('fail', "FAILED"))
+
+            failed, errored = map(len, (result.failures, result.errors))
+            if failed:
+                self.stream.write(" (failures=%s" % c('count', str(failed)))
+            if errored:
+                if failed: self.stream.write(", ")
+                else: self.stream.write("(")
+                self.stream.write("errors=%s" % c('count', str(errored)))
+            self.stream.writeln(")")
+        elif not self.cfg.quiet:
+            self.stream.writeln(c('pass', "OK"))
+        return result
+
+    def _makeResult(self):
+        return CustomTestResult(self.stream, self.descriptions, self.verbosity,
+                                cfg=self.cfg, count=self.count,
+                                hooks=self.hooks)
+
+
+def main(argv):
+    """Main program."""
+
+    # Environment
+    if sys.version_info < (2, 3):
+        print >> sys.stderr, '%s: need Python 2.3 or later' % argv[0]
+        print >> sys.stderr, 'your python is %s' % sys.version
+        return 1
+
+    # Defaults
+    cfg = Options()
+    if not cfg.basedir:
+        cfg.basedir = os.path.join(os.path.dirname(argv[0]), 'src')
+    cfg.basedir = os.path.abspath(cfg.basedir)
+
+    # Figure out terminal size
+    try:
+        import curses
+    except ImportError:
+        pass
+    else:
+        try:
+            curses.setupterm()
+            cols = curses.tigetnum('cols')
+            if cols > 0:
+                cfg.screen_width = cols
+        except curses.error:
+            pass
+
+    # Option processing
+    try:
+        opts, args = getopt.gnu_getopt(argv[1:], 'hvpcCqufwd1s:',
+                               ['list-files', 'list-tests', 'list-hooks',
+                                'level=', 'all-levels', 'coverage',
+                                'search-in=', 'immediate-errors',
+                                'delayed-errors', 'help'])
+    except getopt.error, e:
+        print >> sys.stderr, '%s: %s' % (argv[0], e)
+        print >> sys.stderr, 'run %s -h for help' % argv[0]
+        return 1
+    for k, v in opts:
+        if k in ['-h', '--help']:
+            print __doc__
+            return 0
+        elif k == '-v':
+            cfg.verbosity += 1
+            cfg.quiet = False
+        elif k == '-p':
+            cfg.progress = True
+            cfg.quiet = False
+        elif k == '-c':
+            cfg.colorizer = Colorizer(dark_colormap)
+        elif k == '-C':
+            cfg.colorizer = Colorizer(light_colormap)
+        elif k == '-q':
+            cfg.verbosity = 0
+            cfg.progress = False
+            cfg.quiet = True
+        elif k == '-u':
+            cfg.unit_tests = True
+        elif k == '-f':
+            cfg.functional_tests = True
+        elif k == '-d':
+            cfg.postmortem = True
+        elif k == '-w':
+            cfg.warn_omitted = True
+        elif k == '-1':
+            cfg.first_doctest_failure = True
+        elif k == '--list-files':
+            cfg.list_files = True
+            cfg.run_tests = False
+        elif k == '--list-tests':
+            cfg.list_tests = True
+            cfg.run_tests = False
+        elif k == '--list-hooks':
+            cfg.list_hooks = True
+            cfg.run_tests = False
+        elif k == '--coverage':
+            cfg.coverage = True
+        elif k == '--level':
+            try:
+                cfg.level = int(v)
+            except ValueError:
+                print >> sys.stderr, '%s: invalid level: %s' % (argv[0], v)
+                print >> sys.stderr, 'run %s -h for help' % argv[0]
+                return 1
+        elif k == '--all-levels':
+            cfg.level = None
+        elif k in ('-s', '--search-in'):
+            dir = os.path.abspath(v)
+            if not dir.startswith(cfg.basedir):
+                print >> sys.stderr, ('%s: argument to --search-in (%s) must'
+                                      ' be a subdir of %s'
+                                      % (argv[0], v, cfg.basedir))
+                return 1
+            cfg.search_in += (dir, )
+        elif k == '--immediate-errors':
+            cfg.immediate_errors = True
+        elif k == '--delayed-errors':
+            cfg.immediate_errors = False
+        else:
+            print >> sys.stderr, '%s: invalid option: %s' % (argv[0], k)
+            print >> sys.stderr, 'run %s -h for help' % argv[0]
+            return 1
+    if args:
+        cfg.pathname_regex = args[0]
+    if len(args) > 1:
+        cfg.test_regex = args[1]
+    if len(args) > 2:
+        print >> sys.stderr, '%s: too many arguments: %s' % (argv[0], args[2])
+        print >> sys.stderr, 'run %s -h for help' % argv[0]
+        return 1
+    if not cfg.unit_tests and not cfg.functional_tests:
+        cfg.unit_tests = True
+
+    if not cfg.search_in:
+        cfg.search_in = (cfg.basedir, )
+
+    # Do not print "Imported %d modules in %.3fs" if --list-* was specified
+    # or if quiet mode is enabled.
+    if cfg.quiet or cfg.list_tests or cfg.list_hooks or cfg.list_files:
+        cfg.print_import_time = False
+
+    # Set up the python path
+    sys.path.insert(0, cfg.basedir)
+
+    # Set up tracing before we start importing things
+    tracer = None
+    if cfg.run_tests and cfg.coverage:
+        import trace
+        # trace.py in Python 2.3.1 is buggy:
+        # 1) Despite sys.prefix being in ignoredirs, a lot of system-wide
+        #    modules are included in the coverage reports
+        # 2) Some module file names do not have the first two characters,
+        #    and in general the prefix used seems to be arbitrary
+        # These bugs are fixed in src/trace.py which should be in PYTHONPATH
+        # before the official one.
+        ignoremods = ['test']
+        ignoredirs = [sys.prefix, sys.exec_prefix]
+        tracer = trace.Trace(count=True, trace=False,
+                    ignoremods=ignoremods, ignoredirs=ignoredirs)
+
+    # Configure doctests
+    if cfg.first_doctest_failure:
+        import doctest
+        # The doctest module in Python 2.3 does not have this feature
+        if hasattr(doctest, 'REPORT_ONLY_FIRST_FAILURE'):
+            doctest.set_unittest_reportflags(doctest.REPORT_ONLY_FIRST_FAILURE)
+        # Also apply the flag to zope.testing.doctest, if it exists
+        try:
+            from zope.testing import doctest
+            doctest.set_unittest_reportflags(doctest.REPORT_ONLY_FIRST_FAILURE)
+        except ImportError:
+            pass
+
+    # Make sure we can identify doctests before we start the filtering
+    try:
+        import zope.testing.doctest
+        global DocFileCase_classes
+        DocFileCase_classes += (zope.testing.doctest.DocFileCase,)
+    except ImportError:
+        pass
+
+    # Finding and importing
+    test_files = get_test_files(cfg)
+    if cfg.list_tests or cfg.run_tests:
+        test_cases = get_test_cases(test_files, cfg, tracer=tracer)
+    if cfg.list_hooks or cfg.run_tests:
+        test_hooks = get_test_hooks(test_files, cfg, tracer=tracer)
+
+    # Configure the logging module
+    import logging
+    logging.basicConfig()
+    logging.root.setLevel(logging.CRITICAL)
+
+    # Running
+    success = True
+    if cfg.list_files:
+        baselen = len(cfg.basedir) + 1
+        print "\n".join([fn[baselen:] for fn in test_files])
+    if cfg.list_tests:
+        print "\n".join([name_of_test(test, cfg.basedir)
+                         for test in test_cases])
+    if cfg.list_hooks:
+        print "\n".join([str(hook) for hook in test_hooks])
+    if cfg.run_tests:
+        runner = CustomTestRunner(cfg, test_hooks, count=len(test_cases))
+        suite = unittest.TestSuite()
+        suite.addTests(test_cases)
+        if tracer is not None:
+            success = tracer.runfunc(runner.run, suite).wasSuccessful()
+            results = tracer.results()
+            results.write_results(show_missing=True, coverdir=cfg.coverdir)
+        else:
+            success = runner.run(suite).wasSuccessful()
+
+    # That's all
+    if success:
+        return 0
+    else:
+        return 1
+
+
+if __name__ == '__main__':
+    exitcode = main(sys.argv)
+    sys.exit(exitcode)

File select26.egg-info/PKG-INFO

+Metadata-Version: 1.1
+Name: select26
+Version: 0.1a3
+Summary: Backport of the new select module with epoll and kqueue interface
+Home-page: UNKNOWN
+Author: Christian Heimes
+Author-email: christian@cheimes.de
+License: MIT
+Download-URL: http://pypi.python.org/
+Description: 
+        The select26 extension is a backport of the new API functions of Python
+        2.6 for Python 2.3 to 2.5. It contains object oriented wrappers for epoll
+        (Linux 2.6) and kqueue/kevent (BSD).
+        
+        >>> try:
+        ...     import select26 as select
+        ... except ImportError:
+        ...     import select
+        
+        >>> ep = select.epoll()
+        >>> kq = select.kqueue()
+        
+        This release is based upon Python svn trunk r62498.
+        
+Keywords: select poll epoll kqueue
+Platform: Linux 2.6
+Platform: BSD
+Platform: Mac OS X
+Classifier: Development Status :: 3 - Alpha
+Classifier: Intended Audience :: Developers
+Classifier: License :: OSI Approved :: MIT License
+Classifier: Natural Language :: English
+Classifier: Programming Language :: C
+Classifier: Programming Language :: Python
+Classifier: Operating System :: MacOS :: MacOS X
+Classifier: Operating System :: POSIX :: BSD
+Classifier: Operating System :: POSIX :: Linux
+Classifier: Topic :: System :: Networking
+Provides: select26

File select26.egg-info/SOURCES.txt

+CHANGES
+LICENSE
+MANIFEST.in
+Makefile
+README
+runtests.py
+select26module.c
+setup.py
+select26.egg-info/PKG-INFO
+select26.egg-info/SOURCES.txt
+select26.egg-info/dependency_links.txt
+select26.egg-info/native_libs.txt
+select26.egg-info/top_level.txt
+tests/__init__.py
+tests/test_epoll.py
+tests/test_kqueue.py
+tests/test_select26.py

File select26.egg-info/dependency_links.txt

+

File select26.egg-info/native_libs.txt

+select26.so

File select26.egg-info/top_level.txt

+select26
+tests

File select26module.c

+/* select26 
+ *
+ * Drop in replacement for select module with the new API 
+ * functions from Python 2.6
+ */
+
+#include "Python.h"
+#include <structmember.h>
+
+#if defined(HAVE_POLL_H)
+#include <poll.h>
+#elif defined(HAVE_SYS_POLL_H)
+#include <sys/poll.h>
+#endif
+
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+
+#define SOCKET int
+#ifndef PyVarObject_HEAD_INIT
+#define PyVarObject_HEAD_INIT(type, size)       \
+	PyObject_HEAD_INIT(type) size,
+#endif
+#ifndef Py_TYPE
+#define Py_TYPE(ob) (((PyObject*)(ob))->ob_type)
+#endif
+#ifndef Py_ssize_t
+#define Py_ssize_t ssize_t
+#endif
+#ifndef Py_RETURN_TRUE
+#define Py_RETURN_TRUE return Py_INCREF(Py_True), Py_True
+#endif
+#ifndef Py_RETURN_FALSE
+#define Py_RETURN_FALSE return Py_INCREF(Py_False), Py_False
+#endif
+#ifndef Py_RETURN_NONE
+#define Py_RETURN_NONE return Py_INCREF(Py_None), Py_None
+#endif
+#ifndef Py_CLEAR
+#define Py_CLEAR(op)				\
+	do {					\
+		if (op) {			\
+			PyObject *tmp = (PyObject *)(op);	\
+			(op) = NULL;		\
+			Py_DECREF(tmp);		\
+		}				\
+	} while (0)
+#endif
+#if Py_VERSION_HEX < 0x02040000
+PyObject *
+PyTuple_Pack(Py_ssize_t n, ...)
+{
+        Py_ssize_t i;
+        PyObject *o;
+        PyObject *result;
+        PyObject **items;
+        va_list vargs;
+
+        va_start(vargs, n);
+        result = PyTuple_New(n);
+        if (result == NULL)
+                return NULL;
+        items = ((PyTupleObject *)result)->ob_item;
+        for (i = 0; i < n; i++) {
+                o = va_arg(vargs, PyObject *);
+                Py_INCREF(o);
+                items[i] = o;
+        }
+        va_end(vargs);
+        return result;
+}
+#endif
+
+#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");
+		return NULL;
+	}
+	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) {
+			Py_CLEAR(elist);
+			goto error;
+		}
+		PyList_SET_ITEM(elist, i, etuple);
+	}
+
+    error:
+	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,
+"select26.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)
+	"select26.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),
+		"<select26.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)
+	"select26.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,
+				"select26.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;
+