[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