[pypy-svn] r79856 - in pypy/branch/fast-forward/pypy/module/select: . test

agaynor at codespeak.net agaynor at codespeak.net
Mon Dec 6 21:48:38 CET 2010


Author: agaynor
Date: Mon Dec  6 21:48:35 2010
New Revision: 79856

Added:
   pypy/branch/fast-forward/pypy/module/select/interp_epoll.py
   pypy/branch/fast-forward/pypy/module/select/test/test_epoll.py
Modified:
   pypy/branch/fast-forward/pypy/module/select/__init__.py
Log:
Implemented select.epoll.


Modified: pypy/branch/fast-forward/pypy/module/select/__init__.py
==============================================================================
--- pypy/branch/fast-forward/pypy/module/select/__init__.py	(original)
+++ pypy/branch/fast-forward/pypy/module/select/__init__.py	Mon Dec  6 21:48:35 2010
@@ -1,17 +1,33 @@
 # Package initialisation
 from pypy.interpreter.mixedmodule import MixedModule
+
+import select
 import sys
 
+
 class Module(MixedModule):
     appleveldefs = {
         'error': 'app_select.error',
     }
 
     interpleveldefs = {
-        'poll'  :  'interp_select.poll',
+        'poll'  : 'interp_select.poll',
         'select': 'interp_select.select',
     }
 
+    # TODO: this doesn't feel right...
+    if hasattr(select, "epoll"):
+        interpleveldefs['epoll'] = 'interp_epoll.W_Epoll'
+        symbols = [
+            "EPOLLIN", "EPOLLOUT", "EPOLLPRI", "EPOLLERR", "EPOLLHUP",
+            "EPOLLET", "EPOLLONESHOT", "EPOLLRDNORM", "EPOLLRDBAND",
+            "EPOLLWRNORM", "EPOLLWRBAND", "EPOLLMSG"
+        ]
+        for symbol in symbols:
+            if hasattr(select, symbol):
+                interpleveldefs[symbol] = "space.wrap(%s)" % getattr(select, symbol)
+
+
     def buildloaders(cls):
         from pypy.rlib import rpoll
         for name in rpoll.eventnames:
@@ -19,4 +35,3 @@
             Module.interpleveldefs[name] = "space.wrap(%r)" % value
         super(Module, cls).buildloaders()
     buildloaders = classmethod(buildloaders)
-

Added: pypy/branch/fast-forward/pypy/module/select/interp_epoll.py
==============================================================================
--- (empty file)
+++ pypy/branch/fast-forward/pypy/module/select/interp_epoll.py	Mon Dec  6 21:48:35 2010
@@ -0,0 +1,184 @@
+from __future__ import with_statement
+
+import errno
+import os
+
+from pypy.interpreter.baseobjspace import Wrappable
+from pypy.interpreter.gateway import interp2app, unwrap_spec, ObjSpace, W_Root
+from pypy.interpreter.error import OperationError, wrap_oserror, operationerrfmt
+from pypy.interpreter.typedef import TypeDef, GetSetProperty
+from pypy.module.select.interp_select import as_fd_w
+from pypy.rpython.lltypesystem import lltype, rffi
+from pypy.rpython.tool import rffi_platform
+from pypy.rlib._rsocket_rffi import socketclose, FD_SETSIZE
+from pypy.rlib.rposix import get_errno
+from pypy.translator.tool.cbuild import ExternalCompilationInfo
+
+
+eci = ExternalCompilationInfo(
+    includes = ['sys/epoll.h']
+)
+
+class CConfig:
+    _compilation_info_ = eci
+
+
+CConfig.epoll_data = rffi_platform.Struct("union epoll_data", [
+    ("fd", rffi.INT),
+])
+CConfig.epoll_event = rffi_platform.Struct("struct epoll_event", [
+    ("events", rffi.UINT),
+    ("data", CConfig.epoll_data)
+])
+
+for symbol in ["EPOLL_CTL_ADD", "EPOLL_CTL_MOD", "EPOLL_CTL_DEL"]:
+    setattr(CConfig, symbol, rffi_platform.DefinedConstantInteger(symbol))
+
+cconfig = rffi_platform.configure(CConfig)
+
+epoll_event = cconfig["epoll_event"]
+EPOLL_CTL_ADD = cconfig["EPOLL_CTL_ADD"]
+EPOLL_CTL_MOD = cconfig["EPOLL_CTL_MOD"]
+EPOLL_CTL_DEL = cconfig["EPOLL_CTL_DEL"]
+
+epoll_create = rffi.llexternal(
+    "epoll_create", [rffi.INT], rffi.INT, compilation_info=eci
+)
+epoll_ctl = rffi.llexternal(
+    "epoll_ctl",
+    [rffi.INT, rffi.INT, rffi.INT, lltype.Ptr(epoll_event)],
+    rffi.INT,
+    compilation_info=eci
+)
+epoll_wait = rffi.llexternal(
+    "epoll_wait",
+    [rffi.INT, lltype.Ptr(rffi.CArray(epoll_event)), rffi.INT, rffi.INT],
+    rffi.INT,
+    compilation_info=eci,
+)
+
+def exception_from_errno(space, w_type):
+    errno = get_errno()
+    msg = os.strerror(errno)
+    w_error = space.call_function(w_type, space.wrap(errno), space.wrap(msg))
+    return OperationError(w_type, w_error)
+
+class W_Epoll(Wrappable):
+    def __init__(self, space, epfd):
+        self.space = space
+        self.epfd = epfd
+
+    @unwrap_spec(ObjSpace, W_Root, int)
+    def descr__new__(space, w_subtype, sizehint=-1):
+        if sizehint == -1:
+            sizehint = FD_SETSIZE - 1
+        elif sizehint < 0:
+            raise operationerrfmt(space.w_ValueError,
+                "sizehint must be greater than zero, got %d", sizehint
+            )
+        epfd = epoll_create(sizehint)
+        if epfd < 0:
+            raise exception_from_errno(space, space.w_IOError)
+
+        return space.wrap(W_Epoll(space, epfd))
+
+    @unwrap_spec(ObjSpace, W_Root, int)
+    def descr_fromfd(space, w_cls, fd):
+        return space.wrap(W_Epoll(space, fd))
+
+    def __del__(self):
+        self.close()
+
+    def check_closed(self):
+        if self.epfd < 0:
+            raise OperationError(self.space.w_ValueError,
+                self.space.wrap("I/O operation on closed epoll fd")
+            )
+
+    def close(self):
+        if not self.epfd < 0:
+            socketclose(self.epfd)
+            self.epfd = -1
+
+    def epoll_ctl(self, ctl, w_fd, eventmask, ignore_ebadf=False):
+        fd = as_fd_w(self.space, w_fd)
+        with lltype.scoped_alloc(epoll_event) as ev:
+            ev.c_events = rffi.cast(rffi.UINT, eventmask)
+            ev.c_data.c_fd = fd
+
+            result = epoll_ctl(self.epfd, ctl, fd, ev)
+            if ignore_ebadf and get_errno() == errno.EBADF:
+                result = 0
+            if result < 0:
+                raise exception_from_errno(self.space, self.space.w_IOError)
+
+    def descr_get_closed(space, self):
+        return space.wrap(self.epfd < 0)
+
+    @unwrap_spec("self", ObjSpace)
+    def descr_fileno(self, space):
+        self.check_closed()
+        return space.wrap(self.epfd)
+
+    @unwrap_spec("self", ObjSpace)
+    def descr_close(self, space):
+        self.check_closed()
+        self.close()
+
+    @unwrap_spec("self", ObjSpace, W_Root, int)
+    def descr_register(self, space, w_fd, eventmask=-1):
+        self.check_closed()
+        self.epoll_ctl(EPOLL_CTL_ADD, w_fd, eventmask)
+
+    @unwrap_spec("self", ObjSpace, W_Root)
+    def descr_unregister(self, space, w_fd):
+        self.check_closed()
+        self.epoll_ctl(EPOLL_CTL_DEL, w_fd, 0, ignore_ebadf=True)
+
+    @unwrap_spec("self", ObjSpace, W_Root, int)
+    def descr_modify(self, space, w_fd, eventmask=-1):
+        self.check_closed()
+        self.epoll_ctl(EPOLL_CTL_MOD, w_fd, eventmask)
+
+    @unwrap_spec("self", ObjSpace, float, int)
+    def descr_poll(self, space, timeout=-1.0, maxevents=-1):
+        self.check_closed()
+        if timeout < 0:
+            timeout = -1.0
+        else:
+            timeout *= 1000.0
+
+        if maxevents == -1:
+            maxevents = FD_SETSIZE - 1
+        elif maxevents < 1:
+            raise operationerrfmt(space.w_ValueError,
+                "maxevents must be greater than 0, not %d", maxevents
+            )
+
+        with lltype.scoped_alloc(rffi.CArray(epoll_event), maxevents) as evs:
+            nfds = epoll_wait(self.epfd, evs, maxevents, int(timeout))
+            if nfds < 0:
+                raise exception_from_errno(space, space.w_IOError)
+
+            elist_w = [None] * nfds
+            for i in xrange(nfds):
+                event = evs[i]
+                elist_w[i] = space.newtuple(
+                    [space.wrap(event.c_data.c_fd), space.wrap(event.c_events)]
+                )
+            return space.newlist(elist_w)
+
+
+W_Epoll.typedef = TypeDef("select.epoll",
+    __new__ = interp2app(W_Epoll.descr__new__.im_func),
+    fromfd = interp2app(W_Epoll.descr_fromfd.im_func, as_classmethod=True),
+
+    closed = GetSetProperty(W_Epoll.descr_get_closed),
+    fileno = interp2app(W_Epoll.descr_fileno),
+    close = interp2app(W_Epoll.descr_close),
+    register = interp2app(W_Epoll.descr_register),
+    unregister = interp2app(W_Epoll.descr_unregister),
+    modify = interp2app(W_Epoll.descr_modify),
+    poll = interp2app(W_Epoll.descr_poll),
+)
+W_Epoll.typedef.acceptable_as_base_class = False

Added: pypy/branch/fast-forward/pypy/module/select/test/test_epoll.py
==============================================================================
--- (empty file)
+++ pypy/branch/fast-forward/pypy/module/select/test/test_epoll.py	Mon Dec  6 21:48:35 2010
@@ -0,0 +1,230 @@
+import py
+
+from pypy.conftest import gettestobjspace
+
+
+class AppTestEpoll(object):
+    def setup_class(cls):
+        cls.space = gettestobjspace(usemodules=["select", "_socket", "posix"])
+
+        import errno
+        import select
+
+        if not hasattr(select, "epoll"):
+            py.test.skip("test requires linux 2.6")
+        try:
+            select.epoll()
+        except IOError, e:
+            if e.errno == errno.ENOSYS:
+                py.test.skip("kernel doesn't support epoll()")
+
+    def setup_method(self, meth):
+        self.w_sockets = self.space.wrap([])
+
+    def teardown_method(self, meth):
+        for socket in self.space.unpackiterable(self.w_sockets):
+            self.space.call_method(socket, "close")
+
+    def test_create(self):
+        import select
+
+        ep = select.epoll(16)
+        assert ep.fileno() > 0
+        assert not ep.closed
+        ep.close()
+        assert ep.closed
+        raises(ValueError, ep.fileno)
+
+    def test_badcreate(self):
+        import select
+
+        raises(TypeError, select.epoll, 1, 2, 3)
+        raises(TypeError, select.epoll, 'foo')
+        raises(TypeError, select.epoll, None)
+        raises(TypeError, select.epoll, ())
+        raises(TypeError, select.epoll, ['foo'])
+        raises(TypeError, select.epoll, {})
+
+    def test_add(self):
+        import posix
+        import select
+        import socket
+
+        server_socket = socket.socket()
+        server_socket.bind(('127.0.0.1', 0))
+        server_socket.listen(1)
+        client = socket.socket()
+        client.setblocking(False)
+        raises(socket.error,
+            client.connect, ('127.0.0.1', server_socket.getsockname()[1])
+        )
+        server, addr = server_socket.accept()
+
+        self.sockets.extend([server_socket, client, server])
+
+        ep = select.epoll(2)
+        ep.register(server, select.EPOLLIN | select.EPOLLOUT)
+        ep.register(client, select.EPOLLIN | select.EPOLLOUT)
+        ep.close()
+
+        # adding by object w/ fileno works, too.
+        ep = select.epoll(2)
+        ep.register(server.fileno(), select.EPOLLIN | select.EPOLLOUT)
+        ep.register(client.fileno(), select.EPOLLIN | select.EPOLLOUT)
+        ep.close()
+
+        ep = select.epoll(2)
+        # TypeError: argument must be an int, or have a fileno() method.
+        raises(TypeError, ep.register, object(), select.EPOLLIN | select.EPOLLOUT)
+        raises(TypeError, ep.register, None, select.EPOLLIN | select.EPOLLOUT)
+        # ValueError: file descriptor cannot be a negative integer (-1)
+        raises(ValueError, ep.register, -1, select.EPOLLIN | select.EPOLLOUT)
+        # IOError: [Errno 9] Bad file descriptor
+        raises(IOError, ep.register, 10000, select.EPOLLIN | select.EPOLLOUT)
+        # registering twice also raises an exception
+        ep.register(server, select.EPOLLIN | select.EPOLLOUT)
+        raises(IOError, ep.register, server, select.EPOLLIN | select.EPOLLOUT)
+        ep.close()
+
+    def test_fromfd(self):
+        import errno
+        import select
+        import socket
+
+        server_socket = socket.socket()
+        server_socket.bind(('127.0.0.1', 0))
+        server_socket.listen(1)
+        client = socket.socket()
+        client.setblocking(False)
+        raises(socket.error,
+            client.connect, ('127.0.0.1', server_socket.getsockname()[1])
+        )
+        server, addr = server_socket.accept()
+
+        self.sockets.extend([server_socket, client, server])
+
+
+        ep1 = select.epoll(2)
+        ep2 = select.epoll.fromfd(ep1.fileno())
+
+        ep2.register(server.fileno(), select.EPOLLIN | select.EPOLLOUT)
+        ep2.register(client.fileno(), select.EPOLLIN | select.EPOLLOUT)
+
+        events1 = ep1.poll(1, 4)
+        events2 = ep2.poll(0.9, 4)
+        assert len(events1) == 2
+        assert len(events2) == 2
+        ep1.close()
+
+        exc_info = raises(IOError, ep2.poll, 1, 4)
+        assert exc_info.value.args[0] == errno.EBADF
+
+    def test_control_and_wait(self):
+        import select
+        import socket
+        import time
+
+        server_socket = socket.socket()
+        server_socket.bind(('127.0.0.1', 0))
+        server_socket.listen(1)
+        client = socket.socket()
+        client.setblocking(False)
+        raises(socket.error,
+            client.connect, ('127.0.0.1', server_socket.getsockname()[1])
+        )
+        server, addr = server_socket.accept()
+
+        self.sockets.extend([server_socket, client, server])
+
+
+        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()
+        assert then - now < 0.1
+
+        events.sort()
+        expected = [
+            (client.fileno(), select.EPOLLOUT),
+            (server.fileno(), select.EPOLLOUT)
+        ]
+        expected.sort()
+
+        assert events == expected
+        assert then - now < 0.01
+
+        now = time.time()
+        events = ep.poll(timeout=2.1, maxevents=4)
+        then = time.time()
+        assert not events
+
+        client.send("Hello!")
+        server.send("world!!!")
+
+        now = time.time()
+        events = ep.poll(1, 4)
+        then = time.time()
+        assert then - now < 0.01
+
+        events.sort()
+        expected = [
+            (client.fileno(), select.EPOLLIN | select.EPOLLOUT),
+            (server.fileno(), select.EPOLLIN | select.EPOLLOUT)
+        ]
+        expected.sort()
+
+        assert events == expected
+
+        ep.unregister(client.fileno())
+        ep.modify(server.fileno(), select.EPOLLOUT)
+
+        now = time.time()
+        events = ep.poll(1, 4)
+        then = time.time()
+        assert then - now < 0.01
+
+        expected = [(server.fileno(), select.EPOLLOUT)]
+        assert events == expected
+
+    def test_errors(self):
+        import select
+
+        raises(ValueError, select.epoll, -2)
+        raises(ValueError, select.epoll().register, -1, select.EPOLLIN)
+
+    def test_unregister_closed(self):
+        import select
+        import socket
+        import time
+
+        server_socket = socket.socket()
+        server_socket.bind(('127.0.0.1', 0))
+        server_socket.listen(1)
+        client = socket.socket()
+        client.setblocking(False)
+        raises(socket.error,
+            client.connect, ('127.0.0.1', server_socket.getsockname()[1])
+        )
+        server, addr = server_socket.accept()
+
+        self.sockets.extend([server_socket, client, server])
+
+
+        fd = server.fileno()
+        ep = select.epoll(16)
+        ep.register(server)
+
+        now = time.time()
+        events = ep.poll(1, 4)
+        then = time.time()
+        assert then - now < 0.01
+
+        server.close()
+        ep.unregister(fd)



More information about the Pypy-commit mailing list