import libevent-python-0.1a8

This commit is contained in:
Evan Klitzke 2010-01-25 17:57:35 -08:00
commit 5bfa1dc607
19 changed files with 1406 additions and 0 deletions

20
INSTALL.txt Normal file
View File

@ -0,0 +1,20 @@
# libevent-python
# Copyright (c) 2006 Andy Gross <andy@andygross.org>.
# Copyright (c) 2006 Nick Mathewson.
# See LICENSE.txt for licensing details.
# Installation instructions for libevent-python
1) Install libevent (http://www.monkey.org/~provos/libevent/)
libevent-python tracks the most-recent stable release, available from the
url above.
2) Install libevent-python
% python setup.py install
3) Test libevent-python
% python setup.py test

25
LICENSE.txt Normal file
View File

@ -0,0 +1,25 @@
# Copyright (c) 2006 Andy Gross <andy@andygross.org>
# Copyright (c) 2006 Nick Mathewson
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
#
# * Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# * 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.
# * Neither the name of the University of California, Berkeley nor the
# names of its contributors may be used to endorse or promote products
# derived from this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS AND 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.

18
PKG-INFO Normal file
View File

@ -0,0 +1,18 @@
Metadata-Version: 1.0
Name: libevent-python
Version: 0.1a8
Summary: A CPython extension module wrapping the libevent library
Home-page: http://python-hpio.net/trac/wiki/LibEventPython
Author: Andy Gross
Author-email: andy@andygross.org
License: BSD
Description: UNKNOWN
Platform: UNKNOWN
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: BSD License
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Classifier: Topic :: System :: Networking
Classifier: Topic :: Internet

17
README.txt Normal file
View File

@ -0,0 +1,17 @@
# Copyright (c) 2006 Andy Gross <andy@andygross.org>
# Copyright (c) 2006 Nick Mathewson
# See LICENSE.txt for details.
libevent-python
---------------
libevent-python is a CPython extension module that wraps the lightweight C
library 'libevent', available at http://www.monkey.org/~provos/libevent/.
libevent provides a unified interface to a variety of IO multiplexing
mechanisms (select, poll, kqueue, epoll) and an event loop that supports
timed events and signal handlers.
For usage examples, see the examples/ directory, which should grow as
libevent-python matures.

5
TODO.txt Normal file
View File

@ -0,0 +1,5 @@
* More documentation
* Buffered events
* Twisted integration
* More examples
* Support for libevent-CVS features

117
examples/echo_server.py Normal file
View File

@ -0,0 +1,117 @@
"""
A poorly-factored but kinda-working example of an echo server.
"""
import sys
import socket
import signal
import libevent
class BaseConnection(object):
bufferSize = 2**16
def __init__(self, sock, addr, server):
self.sock = sock
self.addr = addr
self.server = server
self.sock.setblocking(False)
self.buf = []
self.readEvent = libevent.createEvent(
self.sock,libevent.EV_READ|libevent.EV_PERSIST, self._doRead)
self.writeEvent = libevent.createEvent(
self.sock,libevent.EV_WRITE, self._doWrite)
self.startReading()
def startReading(self):
self.readEvent.addToLoop()
def stopReading(self):
self.readEvent.removeFromLoop()
def startWriting(self):
self.writeEvent.addToLoop()
def stopWriting(self):
self.writeEvent.removeFromLoop()
def _doRead(self, fd, events, eventObj):
data = ''
data = self.sock.recv(self.bufferSize)
if not data:
self.server.lostClient(self)
self.stopReading()
self.stopWriting()
self.sock.close()
else:
self.gotData(data)
def _doWrite(self, fd, events, eventObj):
data = "".join(self.buf)
nsent = self.sock.send(data)
data = data[nsent:]
if not data:
self.stopWriting()
self.buf = []
else:
self.buf = [data]
if not self.writeEvent.pending():
self.startWriting()
def write(self, data):
self.buf.append(data)
self.startWriting()
def gotData(self, data):
raise NotImplementedError
class EchoConnection(BaseConnection):
def gotData(self, data):
self.write(data)
class Acceptor(object):
def __init__(self, addr, port, server):
self.addr = addr
self.port = port
self.server = server
self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.sock.setblocking(False)
self.sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
def listen(self):
self.sock.bind((self.addr, self.port))
self.sock.listen(5)
events = libevent.EV_READ|libevent.EV_PERSIST
libevent.createEvent(self.sock, events, self._callback).addToLoop()
def _callback(self, fd, events, eventObj):
sock, addr = self.sock.accept()
self.server.gotClient(sock, addr)
class EchoServer(object):
def __init__(self, addr="127.0.0.1", port=50505):
self.acceptor = Acceptor(addr, port, self)
self.clients = dict()
self.acceptor.listen()
def gotClient(self, sock, addr):
print "Got connection from %s:%s" % addr
client = EchoConnection(sock, addr, self)
self.clients[addr] = client
def lostClient(self, client):
print "Lost connection from %s:%s" % client.addr
client = self.clients[client.addr]
del self.clients[client.addr]
del client
def handleSigInt(signum, events, obj):
libevent.loopExit(0)
raise KeyboardInterrupt
def main():
libevent.createSignalHandler(signal.SIGINT, handleSigInt).addToLoop()
echosrv = EchoServer()
libevent.dispatch()
if __name__ == "__main__":
sys.exit(main())

231
ez_setup.py Executable file
View File

@ -0,0 +1,231 @@
#!/usr/bin/python
"""Bootstrap setuptools installation
If you want to use setuptools in your package's setup.py, just include this
file in the same directory with it, and add this to the top of your setup.py::
from ez_setup import use_setuptools
use_setuptools()
If you want to require a specific version of setuptools, set a download
mirror, or use an alternate download directory, you can do so by supplying
the appropriate options to ``use_setuptools()``.
This file can also be run as a script to install or upgrade setuptools.
"""
import sys
DEFAULT_VERSION = "0.6a10"
DEFAULT_URL = "http://cheeseshop.python.org/packages/%s/s/setuptools/" % sys.version[:3]
md5_data = {
'setuptools-0.5a13-py2.3.egg': '85edcf0ef39bab66e130d3f38f578c86',
'setuptools-0.5a13-py2.4.egg': 'ede4be600e3890e06d4ee5e0148e092a',
'setuptools-0.6a1-py2.3.egg': 'ee819a13b924d9696b0d6ca6d1c5833d',
'setuptools-0.6a1-py2.4.egg': '8256b5f1cd9e348ea6877b5ddd56257d',
'setuptools-0.6a10-py2.3.egg': '162d8357f1aff2b0349c6c247ee62987',
'setuptools-0.6a10-py2.4.egg': '803a2d8db501c1ac3b5b6fb4e907f788',
'setuptools-0.6a10dev_r42346-py2.3.egg': 'a7899272cfceb6aa60094ae8928b8077',
'setuptools-0.6a10dev_r42346-py2.4.egg': '5d42a64adca9aedb409f83ecf22156a5',
'setuptools-0.6a2-py2.3.egg': 'b98da449da411267c37a738f0ab625ba',
'setuptools-0.6a2-py2.4.egg': 'be5b88bc30aed63fdefd2683be135c3b',
'setuptools-0.6a3-py2.3.egg': 'ee0e325de78f23aab79d33106dc2a8c8',
'setuptools-0.6a3-py2.4.egg': 'd95453d525a456d6c23e7a5eea89a063',
'setuptools-0.6a4-py2.3.egg': 'e958cbed4623bbf47dd1f268b99d7784',
'setuptools-0.6a4-py2.4.egg': '7f33c3ac2ef1296f0ab4fac1de4767d8',
'setuptools-0.6a5-py2.3.egg': '748408389c49bcd2d84f6ae0b01695b1',
'setuptools-0.6a5-py2.4.egg': '999bacde623f4284bfb3ea77941d2627',
'setuptools-0.6a6-py2.3.egg': '7858139f06ed0600b0d9383f36aca24c',
'setuptools-0.6a6-py2.4.egg': 'c10d20d29acebce0dc76219dc578d058',
'setuptools-0.6a7-py2.3.egg': 'cfc4125ddb95c07f9500adc5d6abef6f',
'setuptools-0.6a7-py2.4.egg': 'c6d62dab4461f71aed943caea89e6f20',
'setuptools-0.6a8-py2.3.egg': '2f18eaaa3f544f5543ead4a68f3b2e1a',
'setuptools-0.6a8-py2.4.egg': '799018f2894f14c9f8bcb2b34e69b391',
'setuptools-0.6a9-py2.3.egg': '8e438ad70438b07b0d8f82cae42b278f',
'setuptools-0.6a9-py2.4.egg': '8f6e01fc12fb1cd006dc0d6c04327ec1',
}
import sys, os
def _validate_md5(egg_name, data):
if egg_name in md5_data:
from md5 import md5
digest = md5(data).hexdigest()
if digest != md5_data[egg_name]:
print >>sys.stderr, (
"md5 validation of %s failed! (Possible download problem?)"
% egg_name
)
sys.exit(2)
return data
def use_setuptools(
version=DEFAULT_VERSION, download_base=DEFAULT_URL, to_dir=os.curdir,
download_delay=15
):
"""Automatically find/download setuptools and make it available on sys.path
`version` should be a valid setuptools version number that is available
as an egg for download under the `download_base` URL (which should end with
a '/'). `to_dir` is the directory where setuptools will be downloaded, if
it is not already available. If `download_delay` is specified, it should
be the number of seconds that will be paused before initiating a download,
should one be required. If an older version of setuptools is installed,
this routine will print a message to ``sys.stderr`` and raise SystemExit in
an attempt to abort the calling script.
"""
try:
import setuptools
if setuptools.__version__ == '0.0.1':
print >>sys.stderr, (
"You have an obsolete version of setuptools installed. Please\n"
"remove it from your system entirely before rerunning this script."
)
sys.exit(2)
except ImportError:
egg = download_setuptools(version, download_base, to_dir, download_delay)
sys.path.insert(0, egg)
import setuptools; setuptools.bootstrap_install_from = egg
import pkg_resources
try:
pkg_resources.require("setuptools>="+version)
except pkg_resources.VersionConflict:
# XXX could we install in a subprocess here?
print >>sys.stderr, (
"The required version of setuptools (>=%s) is not available, and\n"
"can't be installed while this script is running. Please install\n"
" a more recent version first."
) % version
sys.exit(2)
def download_setuptools(
version=DEFAULT_VERSION, download_base=DEFAULT_URL, to_dir=os.curdir,
delay = 15
):
"""Download setuptools from a specified location and return its filename
`version` should be a valid setuptools version number that is available
as an egg for download under the `download_base` URL (which should end
with a '/'). `to_dir` is the directory where the egg will be downloaded.
`delay` is the number of seconds to pause before an actual download attempt.
"""
import urllib2, shutil
egg_name = "setuptools-%s-py%s.egg" % (version,sys.version[:3])
url = download_base + egg_name
saveto = os.path.join(to_dir, egg_name)
src = dst = None
if not os.path.exists(saveto): # Avoid repeated downloads
try:
from distutils import log
if delay:
log.warn("""
---------------------------------------------------------------------------
This script requires setuptools version %s to run (even to display
help). I will attempt to download it for you (from
%s), but
you may need to enable firewall access for this script first.
I will start the download in %d seconds.
(Note: if this machine does not have network access, please obtain the file
%s
and place it in this directory before rerunning this script.)
---------------------------------------------------------------------------""",
version, download_base, delay, url
); from time import sleep; sleep(delay)
log.warn("Downloading %s", url)
src = urllib2.urlopen(url)
# Read/write all in one block, so we don't create a corrupt file
# if the download is interrupted.
data = _validate_md5(egg_name, src.read())
dst = open(saveto,"wb"); dst.write(data)
finally:
if src: src.close()
if dst: dst.close()
return os.path.realpath(saveto)
def main(argv, version=DEFAULT_VERSION):
"""Install or upgrade setuptools and EasyInstall"""
try:
import setuptools
except ImportError:
import tempfile, shutil
tmpdir = tempfile.mkdtemp(prefix="easy_install-")
try:
egg = download_setuptools(version, to_dir=tmpdir, delay=0)
sys.path.insert(0,egg)
from setuptools.command.easy_install import main
main(list(argv)+[egg])
finally:
shutil.rmtree(tmpdir)
else:
if setuptools.__version__ == '0.0.1':
# tell the user to uninstall obsolete version
use_setuptools(version)
req = "setuptools>="+version
import pkg_resources
try:
pkg_resources.require(req)
except pkg_resources.VersionConflict:
try:
from setuptools.command.easy_install import main
except ImportError:
from easy_install import main
main(list(argv)+[download_setuptools(delay=0)])
sys.exit(0) # try to force an exit
else:
if argv:
from setuptools.command.easy_install import main
main(argv)
else:
print "Setuptools version",version,"or greater has been installed."
print '(Run "ez_setup.py -U setuptools" to reinstall or upgrade.)'
def update_md5(filenames):
"""Update our built-in md5 registry"""
import re
from md5 import md5
for name in filenames:
base = os.path.basename(name)
f = open(name,'rb')
md5_data[base] = md5(f.read()).hexdigest()
f.close()
data = [" %r: %r,\n" % it for it in md5_data.items()]
data.sort()
repl = "".join(data)
import inspect
srcfile = inspect.getsourcefile(sys.modules[__name__])
f = open(srcfile, 'rb'); src = f.read(); f.close()
match = re.search("\nmd5_data = {\n([^}]+)}", src)
if not match:
print >>sys.stderr, "Internal error!"
sys.exit(2)
src = src[:match.start(1)] + repl + src[match.end(1):]
f = open(srcfile,'w')
f.write(src)
f.close()
if __name__=='__main__':
if len(sys.argv)>2 and sys.argv[1]=='--md5update':
update_md5(sys.argv[2:])
else:
main(sys.argv[1:])

22
libevent/__init__.py Normal file
View File

@ -0,0 +1,22 @@
# Copyright (c) 2006 Andy Gross <andy@andygross.org>
# Copyright (c) 2006 Nick Mathewson
# See LICENSE.txt for details.
from event import *
def createEvent(fd, events, callback):
return DefaultEventBase.createEvent(fd, events, callback)
def createTimer(callback):
return DefaultEventBase.createTimer(callback)
def createSignalHandler(signum, callback):
return DefaultEventBase.createSignalHandler(signum, callback)
def loop(flags=0):
return DefaultEventBase.loop(flags)
def loopExit(seconds):
return DefaultEventBase.loopExit(seconds)
def dispatch():
return DefaultEventBase.dispatch()

705
libevent/eventmodule.c Normal file
View File

@ -0,0 +1,705 @@
/*
* eventmodule.c: a wrapper for libevent (http://monkey.org/~provos/libevent/)
* Copyright (c) 2006 Andy Gross <andy@andygross.org>
* Copyright (c) 2006 Nick Mathewson
* See LICENSE.txt for licensing information.
*/
#include <sys/time.h>
#include <sys/types.h>
#include <event.h>
#include <Python.h>
#include <structmember.h>
#define DEFAULT_NUM_PRIORITIES 3
/*
* EventBaseObject wraps a (supposedly) thread-safe libevent dispatch context.
*/
typedef struct EventBaseObject {
PyObject_HEAD
struct event_base *ev_base;
} EventBaseObject;
/* Forward declaration of CPython type object */
static PyTypeObject EventBase_Type;
/*
* EventObject wraps a libevent 'struct event'
*/
typedef struct EventObject {
PyObject_HEAD
struct event ev;
EventBaseObject *eventBase;
PyObject *callback;
} EventObject;
/* Forward declaration of CPython type object */
static PyTypeObject Event_Type;
/* EventObject prototypes */
static PyObject *Event_New(PyTypeObject *, PyObject *, PyObject *);
static int Event_Init(EventObject *, PyObject *, PyObject *);
/* Singleton default event base */
static EventBaseObject *defaultEventBase;
/* Reference to the logging callback */
static PyObject *logCallback;
/* Error Objects */
PyObject *EventErrorObject;
/* Typechecker */
int EventBase_Check(PyObject *o) {
return ((o->ob_type) == &EventBase_Type);
}
/* Construct a new EventBaseObject */
static PyObject *EventBase_New(PyTypeObject *type, PyObject *args,
PyObject *kwds)
{
EventBaseObject *self = NULL;
assert(type != NULL && type->tp_alloc != NULL);
self = (EventBaseObject *)type->tp_alloc(type, 0);
if (self != NULL) {
self->ev_base = event_init();
if (self->ev_base == NULL) {
return NULL;
}
}
return (PyObject *)self;
}
/* EventBaseObject initializer */
static int EventBase_Init(EventBaseObject *self, PyObject *args,
PyObject *kwargs)
{
static char *kwlist[] = {"numPriorities", NULL};
int numPriorities = 0;
if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|i:event", kwlist,
&numPriorities))
return -1;
if (!numPriorities)
numPriorities = DEFAULT_NUM_PRIORITIES;
if ( (event_base_priority_init(self->ev_base, numPriorities)) < 0) {
return -1;
}
return 0;
}
/* EventBaseObject destructor */
static void EventBase_Dealloc(EventBaseObject *obj) {
obj->ob_type->tp_free((PyObject *)obj);
}
/* EventBaseObject methods */
PyDoc_STRVAR(EventBase_LoopDoc,
"loop(self, [flags=0])\n\
\n\
Perform one iteration of the event loop. Valid flags arg EVLOOP_NONBLOCK \n\
and EVLOOP_ONCE.");
static PyObject *EventBase_Loop(EventBaseObject *self, PyObject *args,
PyObject *kwargs)
{
static char *kwlist[] = {"flags", NULL};
int flags = 0;
int rv = 0;
if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|i:loop", kwlist, &flags))
return NULL;
rv = event_base_loop(self->ev_base, flags);
return PyInt_FromLong(rv);
}
PyDoc_STRVAR(EventBase_LoopExitDoc,
"loopExit(self, seconds=0)\n\
\n\
Cause the event loop to exit after <seconds> seconds.");
static PyObject *EventBase_LoopExit(EventBaseObject *self, PyObject *args,
PyObject *kwargs) {
static char * kwlist[] = {"seconds", NULL};
struct timeval tv;
int rv = 0;
double exitAfterSecs = 0;
if (!PyArg_ParseTupleAndKeywords(args, kwargs, "d:loopExit",
kwlist, &exitAfterSecs))
return NULL;
tv.tv_sec = (long) exitAfterSecs;
tv.tv_usec = (exitAfterSecs - (long) exitAfterSecs) * 1000000;
rv = event_base_loopexit(self->ev_base, &tv);
return PyInt_FromLong(rv);
}
PyDoc_STRVAR(EventBase_DispatchDoc,
"dispatch(self)\n\
\n\
Run the main dispatch loop associated with this event base. This function\n\
only terminates when no events remain, or the loop is terminated via an \n\
explicit call to EventBase.loopExit() or via a signal.");
static PyObject *EventBase_Dispatch(EventBaseObject *self, PyObject *args,
PyObject *kwargs) {
int rv = event_base_dispatch(self->ev_base);
return PyInt_FromLong(rv);
}
PyDoc_STRVAR(EventBase_CreateEventDoc,
"createEvent(self, fd, events, callback)\n\
\n\
Create a new Event object for the given file descriptor that will call\n\
<callback> with a 3-tuple of (fd, events, eventObject) when the event\n\
fires. The first argument, fd, can be either an integer file descriptor\n\
or a 'file-like' object with a fileno() method.");
static EventObject *EventBase_CreateEvent(EventBaseObject *self,
PyObject *args, PyObject *kwargs)
{
EventObject *newEvent = NULL;
newEvent = (EventObject *)Event_New(&Event_Type,NULL,NULL);
if (Event_Init(newEvent, args, kwargs) < 0)
return NULL;
if (PyObject_CallMethod((PyObject *)newEvent,
"setEventBase", "O", self) == NULL)
return NULL;
return newEvent;
}
PyDoc_STRVAR(EventBase_CreateTimerDoc,
"createTimer(self, callback) -> new timer Event\n\
\n\
Create a new timer object that will call <callback>. The timeout is not\n\
specified here, but rather via the Event.addToLoop([timeout]) method");
static EventObject *EventBase_CreateTimer(EventBaseObject *self,
PyObject *args, PyObject *kwargs)
{
static char *kwlist[] = {"callback", NULL};
EventObject *newTimer = NULL;
PyObject *callback = NULL;
if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O:createTimer",
kwlist, &callback))
return NULL;
newTimer = (EventObject *)PyObject_CallMethod((PyObject *)self,
"createEvent",
"OiO", Py_None, EV_TIMEOUT,
callback);
return newTimer;
}
PyDoc_STRVAR(EventBase_CreateSignalHandlerDoc,
"createSignalHandler(self, signum, callback) -> new signal handler Event\n\
\n\
Create a new signal handler object that will call <callback> when the signal\n\
is received. Signal handlers are by default persistent - you must manually\n\
remove them with removeFromLoop().");
static EventObject *EventBase_CreateSignalHandler(EventBaseObject *self,
PyObject *args,
PyObject *kwargs) {
static char *kwlist[] = {"signal", "callback", NULL};
EventObject *newSigHandler = NULL;
PyObject *callback = NULL;
int sig = 0;
if (!PyArg_ParseTupleAndKeywords(args, kwargs, "iO:createSignalHandler",
kwlist, &sig, &callback))
return NULL;
newSigHandler = (EventObject *)PyObject_CallMethod((PyObject *)self,
"createEvent",
"iiO",
sig,
EV_SIGNAL|EV_PERSIST,
callback);
return newSigHandler;
}
static PyGetSetDef EventBase_Properties[] = {
{NULL},
};
static PyMemberDef EventBase_Members[] = {
{NULL},
};
static PyMethodDef EventBase_Methods[] = {
{"loop", (PyCFunction)EventBase_Loop,
METH_VARARGS|METH_KEYWORDS, EventBase_LoopDoc},
{"loopExit", (PyCFunction)EventBase_LoopExit,
METH_VARARGS|METH_KEYWORDS, EventBase_LoopExitDoc},
{"createEvent", (PyCFunction)EventBase_CreateEvent,
METH_VARARGS|METH_KEYWORDS, EventBase_CreateEventDoc},
{"createSignalHandler", (PyCFunction)EventBase_CreateSignalHandler,
METH_VARARGS|METH_KEYWORDS, EventBase_CreateSignalHandlerDoc},
{"createTimer", (PyCFunction)EventBase_CreateTimer,
METH_VARARGS|METH_KEYWORDS, EventBase_CreateTimerDoc},
{"dispatch", (PyCFunction)EventBase_Dispatch,
METH_NOARGS, EventBase_DispatchDoc},
{NULL},
};
static PyTypeObject EventBase_Type = {
PyObject_HEAD_INIT(&PyType_Type)
0,
"event.EventBase", /*tp_name*/
sizeof(EventBaseObject), /*tp_basicsize*/
0, /*tp_itemsize*/
/* methods */
(destructor)EventBase_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*/
PyObject_GenericSetAttr, /*tp_setattro*/
0, /*tp_as_buffer*/
Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/
0, /*tp_doc*/
0, /*tp_traverse*/
0, /*tp_clear*/
0, /*tp_richcompare*/
0, /*tp_weaklistoffset*/
0, /*tp_iter*/
0, /*tp_iternext*/
EventBase_Methods, /*tp_methods*/
EventBase_Members, /*tp_members*/
EventBase_Properties, /*tp_getset*/
0, /*tp_base*/
0, /*tp_dict*/
0, /*tp_descr_get*/
0, /*tp_descr_set*/
0, /*tp_dictoffset*/
(initproc)EventBase_Init, /*tp_init*/
PyType_GenericAlloc, /*tp_alloc*/
EventBase_New, /*tp_new*/
PyObject_Del, /*tp_free*/
0, /*tp_is_gc*/
};
/* Typechecker */
int Event_Check(PyObject *o) {
return ((o->ob_type) == &Event_Type);
}
/* Construct a new EventObject */
static PyObject *Event_New(PyTypeObject *type, PyObject *args,
PyObject *kwargs)
{
EventObject *self = NULL;
assert(type != NULL && type->tp_alloc != NULL);
self = (EventObject *)type->tp_alloc(type, 0);
self->eventBase = NULL;
return (PyObject *)self;
}
/* Callback thunk. */
static void __libevent_ev_callback(int fd, short events, void *arg) {
EventObject *ev = arg;
PyObject *result;
PyObject *tuple = PyTuple_New(3);
PyTuple_SET_ITEM(tuple, 0, PyInt_FromLong(fd));
PyTuple_SET_ITEM(tuple, 1, PyInt_FromLong(events));
PyTuple_SET_ITEM(tuple, 2, (PyObject *) ev);
Py_INCREF((PyObject *) ev);
result = PyObject_Call(ev->callback, tuple, NULL);
Py_DECREF((PyObject *) ev);
//Py_DECREF(tuple);
if (result) {
Py_DECREF(result);
}
else {
/*
* The callback raised an exception. This usually isnt a problem because
* the callback's caller is in Python-land. Here, we don't have many
* good options. For now, we just print the exception. The commented
* out code below is supposed to asynchronously raise an exception in
* the main thread, but that doesn't work if libevent is blocked on
* an I/O call like select() or kevent(). We could terminate the
* event loop from here, but that seems a little drastic. Somehow,
* we should move the callback invocation to Python. I think.
*/
/*
PyThreadState *ts = PyThreadState_Get();
int r = PyThreadState_SetAsyncExc(ts->thread_id, EventErrorObject);
printf("%d\n", r);
*/
PyErr_WriteUnraisable(ev->callback);
}
}
/* EventObject initializer */
static int Event_Init(EventObject *self, PyObject *args, PyObject *kwargs) {
int fd = -1;
PyObject *fdObj = NULL;
int events = 0;
PyObject *callback = NULL;
static char *kwlist[] = {"fd", "events", "callback", NULL};
if (!PyArg_ParseTupleAndKeywords(args, kwargs, "OiO:event", kwlist,
&fdObj, &events, &callback))
return -1;
if (!PyCallable_Check(callback)) {
PyErr_SetString(EventErrorObject,"callback argument must be callable");
return -1;
}
if (fdObj != Py_None) {
if ( (fd = PyObject_AsFileDescriptor(fdObj)) == -1 ) {
return -1;
}
}
event_set(&self->ev, fd, events, __libevent_ev_callback, self);
if (! event_initialized(&self->ev) )
return -1;
Py_INCREF(callback);
self->callback = callback;
return 0;
}
PyDoc_STRVAR(Event_SetPriorityDoc,
"setPriority(self, priority)\n\
\n\
Set the priority for this event.");
static PyObject *Event_SetPriority(EventObject *self, PyObject *args,
PyObject *kwargs)
{
static char *kwlist[] = {"priority", NULL};
int priority = 0;
if (!PyArg_ParseTupleAndKeywords(args, kwargs , "|i:setPriority",
kwlist, &priority))
return NULL;
if (event_priority_set(&self->ev, priority) < 0) {
PyErr_SetString(EventErrorObject,
"error setting event priority - event is either already active or priorities are not enabled");
return NULL;
}
Py_INCREF(Py_None);
return Py_None;
}
PyDoc_STRVAR(Event_AddToLoopDoc,
"addToLoop(self, timeout=-1)\n\
\n\
Add this event to the event loop, with a timeout of <timeout> seconds.\n\
A timeout value of -1 seconds causes the event to remain in the loop \n\
until it fires or is manually removed with removeFromLoop().");
static PyObject *Event_AddToLoop(EventObject *self, PyObject *args,
PyObject *kwargs) {
double timeout = -1.0;
struct timeval tv;
static char *kwlist[] = {"timeout", NULL};
int rv;
if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|d:addToLoop", kwlist,
&timeout))
return NULL;
if (timeout >= 0.0) {
tv.tv_sec = (long) timeout;
tv.tv_usec = (timeout - (long) timeout) * 1000000;
rv = event_add(&((EventObject *) self)->ev, &tv);
}
else {
rv = event_add(&((EventObject *) self)->ev, NULL);
}
if (rv != 0) {
PyErr_SetFromErrno(EventErrorObject);
return NULL;
}
Py_INCREF(self);
Py_INCREF(Py_None);
return Py_None;
}
PyDoc_STRVAR(Event_RemoveFromLoopDoc,
"removeFromLoop(self)\n\
\n\
Remove the event from the event loop.");
static PyObject *Event_RemoveFromLoop(EventObject *self, PyObject *args,
PyObject *kwargs) {
if (event_del(&self->ev) < 0) {
PyErr_SetFromErrno(EventErrorObject);
return NULL;
}
Py_DECREF(self);
Py_INCREF(Py_None);
return Py_None;
}
static PyObject *Event_SetEventBase(EventObject *self, PyObject *args,
PyObject *kwargs) {
static char *kwlist[] = {"eventBase", NULL};
PyObject *eventBase;
int rv = 0;
if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O", kwlist, &eventBase))
return NULL;
if (!EventBase_Check(eventBase)) {
PyErr_SetString(EventErrorObject, "argument is not an EventBase object");
return NULL;
}
rv = event_base_set(((EventBaseObject *)eventBase)->ev_base, &self->ev);
if (rv < 0) {
PyErr_SetString(EventErrorObject, "unable to set event base");
return NULL;
}
if (self->eventBase != NULL) {
Py_XDECREF(self->eventBase);
}
Py_INCREF(eventBase);
self->eventBase = (EventBaseObject *)eventBase;
Py_INCREF(Py_None);
return Py_None;
}
PyDoc_STRVAR(Event_PendingDoc,
"pending(self)\n\
\n\
Returns the event flags set for this event OR'd together.");
static PyObject *Event_Pending(EventObject *self, PyObject *args,
PyObject *kwargs) {
int flags;
flags = event_pending(&((EventObject *) self)->ev,
EV_TIMEOUT | EV_READ | EV_WRITE | EV_SIGNAL, NULL);
return PyInt_FromLong(flags);
}
PyDoc_STRVAR(Event_GetTimeoutDoc,
"getTimeout(self)\n\
\n\
Returns the expiration time of this event.");
static PyObject *Event_GetTimeout(EventObject *self, PyObject *args,
PyObject *kwargs) {
double d;
struct timeval tv;
tv.tv_sec = -1;
event_pending(&((EventObject *) self)->ev, 0, &tv);
if (tv.tv_sec > -1) {
d = tv.tv_sec + (tv.tv_usec / 1000000.0);
return PyFloat_FromDouble(d);
}
Py_INCREF(Py_None);
return Py_None;
}
PyDoc_STRVAR(Event_FilenoDoc,
"fileno(self)\n\
\n\
Return the integer file descriptor number associated with this event.\n\
Not especially meaningful for signal or timer events.");
static PyObject *Event_Fileno(EventObject *self, PyObject *args,
PyObject *kwargs) {
return PyInt_FromLong(self->ev.ev_fd);
}
/* EventObject destructor */
static void Event_Dealloc(EventObject *obj) {
Py_XDECREF(obj->eventBase);
Py_XDECREF(obj->callback);
obj->ob_type->tp_free((PyObject *)obj);
}
static PyObject *Event_Repr(EventObject *self) {
char buf[512];
PyOS_snprintf(buf, sizeof(buf),
"<event object, fd=%ld, events=%d>",
(long) self->ev.ev_fd,
(int) self->ev.ev_events);
return PyString_FromString(buf);
}
#define OFF(x) offsetof(EventObject, x)
static PyMemberDef Event_Members[] = {
{"eventBase", T_OBJECT, OFF(eventBase),
RO, "The EventBase for this event object"},
{"callback", T_OBJECT, OFF(callback),
RO, "The callback for this event object"},
{"events", T_SHORT, OFF(ev.ev_events),
RO, "Events registered for this event object"},
{"numCalls", T_SHORT, OFF(ev.ev_ncalls),
RO, "Number of times this event has been called"},
{"priority", T_INT, OFF(ev.ev_pri),
RO, "Event priority"},
{"flags", T_INT, OFF(ev.ev_flags),
RO, "Event flags (internal)"},
{NULL}
};
#undef OFF
static PyGetSetDef Event_Properties[] = {
{NULL},
};
static PyMethodDef Event_Methods[] = {
{"addToLoop", (PyCFunction)Event_AddToLoop,
METH_VARARGS|METH_KEYWORDS, Event_AddToLoopDoc},
{"removeFromLoop", (PyCFunction)Event_RemoveFromLoop,
METH_NOARGS, Event_RemoveFromLoopDoc},
{"fileno", (PyCFunction)Event_Fileno,
METH_NOARGS, Event_FilenoDoc},
{"setPriority", (PyCFunction)Event_SetPriority,
METH_VARARGS|METH_KEYWORDS, Event_SetPriorityDoc},
{"setEventBase", (PyCFunction)Event_SetEventBase,
METH_VARARGS|METH_KEYWORDS},
{"pending", (PyCFunction)Event_Pending,
METH_NOARGS, Event_PendingDoc},
{"getTimeout", (PyCFunction)Event_GetTimeout,
METH_NOARGS, Event_GetTimeoutDoc},
{NULL},
};
static PyTypeObject Event_Type = {
PyObject_HEAD_INIT(&PyType_Type)
0,
"event.Event", /*tp_name*/
sizeof(EventObject), /*tp_basicsize*/
0, /*tp_itemsize*/
/* methods */
(destructor)Event_Dealloc, /*tp_dealloc*/
0, /*tp_print*/
0, /*tp_getattr*/
0, /*tp_setattr*/
0, /*tp_compare*/
(reprfunc)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*/
PyObject_GenericGetAttr, /*tp_getattro*/
PyObject_GenericSetAttr, /*tp_setattro*/
0, /*tp_as_buffer*/
Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/
0, /*tp_doc*/
0, /*tp_traverse*/
0, /*tp_clear*/
0, /*tp_richcompare*/
0, /*tp_weaklistoffset*/
0, /*tp_iter*/
0, /*tp_iternext*/
Event_Methods, /*tp_methods*/
Event_Members, /*tp_members*/
Event_Properties, /*tp_getset*/
0, /*tp_base*/
0, /*tp_dict*/
0, /*tp_descr_get*/
0, /*tp_descr_set*/
0, /*tp_dictoffset*/
(initproc)Event_Init, /*tp_init*/
PyType_GenericAlloc, /*tp_alloc*/
Event_New, /*tp_new*/
PyObject_Del, /*tp_free*/
0, /*tp_is_gc*/
};
static PyObject *EventModule_setLogCallback(PyObject *self, PyObject *args,
PyObject *kwargs) {
static char *kwlist[] = {"callback", NULL};
if (!PyArg_ParseTupleAndKeywords(args,kwargs,"O:setLogCallback", kwlist,
&logCallback))
return NULL;
if (!PyCallable_Check(logCallback)) {
PyErr_SetString(EventErrorObject, "log callback is not a callable");
logCallback = NULL;
return NULL;
}
Py_INCREF(Py_None);
return Py_None;
}
static PyMethodDef EventModule_Functions[] = {
{"setLogCallback", (PyCFunction)EventModule_setLogCallback,
METH_VARARGS|METH_KEYWORDS},
{NULL},
};
#define ADDCONST(mod, name, const) PyModule_AddIntConstant(mod, name, const)
DL_EXPORT(void) initevent(void)
{
PyObject *m, *d;
m = Py_InitModule("event", EventModule_Functions);
d = PyModule_GetDict(m);
if (EventErrorObject == NULL) {
EventErrorObject = PyErr_NewException("libevent.EventError",
NULL, NULL);
if (EventErrorObject == NULL)
return;
}
Py_INCREF(EventErrorObject);
PyModule_AddObject(m, "EventError", EventErrorObject);
if (PyType_Ready(&EventBase_Type) < 0) {
return;
}
PyModule_AddObject(m, "EventBase", (PyObject *)&EventBase_Type);
if (PyType_Ready(&Event_Type) < 0)
return;
PyModule_AddObject(m, "Event", (PyObject *)&Event_Type);
defaultEventBase = (EventBaseObject *)EventBase_New(&EventBase_Type,
NULL, NULL);
if (defaultEventBase == NULL) {
PyErr_SetString(EventErrorObject,
"error: couldn't create default event base");
return;
}
if (EventBase_Init(defaultEventBase, PyTuple_New(0), NULL) < 0) {
PyErr_SetString(EventErrorObject,
"error: couldn't initialize default event base");
return;
}
PyModule_AddObject(m, "DefaultEventBase", (PyObject *)defaultEventBase);
/* Add constants to the module */
ADDCONST(m, "EV_READ", EV_READ);
ADDCONST(m, "EV_WRITE", EV_WRITE);
ADDCONST(m, "EV_TIMEOUT", EV_TIMEOUT);
ADDCONST(m, "EV_SIGNAL", EV_SIGNAL);
ADDCONST(m, "EV_PERSIST", EV_PERSIST);
ADDCONST(m, "EVLOOP_ONCE", EVLOOP_ONCE);
ADDCONST(m, "EVLOOP_NONBLOCK", EVLOOP_NONBLOCK);
PyModule_AddObject(m, "LIBEVENT_VERSION",
PyString_FromString(event_get_version()));
PyModule_AddObject(m, "LIBEVENT_METHOD",
PyString_FromString(event_get_method()));
}

12
libevent/tests/TestAll.py Normal file
View File

@ -0,0 +1,12 @@
""" Runs all unit tests for the libevent package. """
# Copyright (c) 2006 Andy Gross. See LICENSE.txt for details.
import sys
import unittest
from TestEvent import *
from TestEventBase import *
from TestPackage import *
if __name__=='__main__':
unittest.main()

125
libevent/tests/TestEvent.py Normal file
View File

@ -0,0 +1,125 @@
import unittest
import tempfile
import sys
import os
import time
import signal
import socket
import libevent
def passThroughEventCallback(fd, events, eventObj):
return fd, events, eventObj
def makeEvent(fd=0, events=libevent.EV_WRITE):
return libevent.createEvent(fd, events, passThroughEventCallback)
class EventConstructionTests(unittest.TestCase):
def testValidConstructionWithIntegerFd(self):
event = makeEvent()
def testEventsGetDefaultEventBase(self):
event = makeEvent()
self.assertEqual(event.eventBase, libevent.DefaultEventBase)
def testSettingCustomEventBase(self):
event = makeEvent()
newEventBase = libevent.EventBase()
event.setEventBase(newEventBase)
self.assertEqual(event.eventBase, newEventBase)
def testInvalidConstructionNonCallableCallback(self):
self.assertRaises(libevent.EventError, libevent.Event, sys.stdout,
libevent.EV_WRITE, "i'm not a callable, thats fer shure")
def testValidObjectStructure(self):
event = makeEvent(sys.stdout)
self.assertEqual(event.fileno(), sys.stdout.fileno())
self.assertEqual(event.callback, passThroughEventCallback)
self.assertEqual(event.events & libevent.EV_WRITE, libevent.EV_WRITE)
self.assertEqual(event.numCalls, 0)
def testValidConstructionWithFileLikeObject(self):
fp = tempfile.TemporaryFile()
event = libevent.Event(fp, libevent.EV_WRITE, passThroughEventCallback)
def testCreateTimer(self):
timer = libevent.createTimer(passThroughEventCallback)
def testTimerFlags(self):
timer = libevent.createTimer(passThroughEventCallback)
timer.addToLoop(1)
self.assertEqual(timer.pending() & libevent.EV_TIMEOUT, True)
timer.removeFromLoop()
self.assertEqual(timer.pending() & libevent.EV_TIMEOUT, False)
class EventPriorityTests(unittest.TestCase):
def testSettingPriority(self):
e = makeEvent()
e.setPriority(2)
self.assertEqual(e.priority, 2)
def testDefaultPriorityIsMiddle(self):
e = makeEvent()
self.assertEqual(e.priority, 1)
def testSettingCustomPriorityCount(self):
eventBase = libevent.EventBase(numPriorities=420)
e = eventBase.createEvent(fd=0, events=libevent.EV_READ, callback=passThroughEventCallback)
self.assertEqual(e.priority, 210)
def testSettingPriorityAfterLoopAdd(self):
e = makeEvent()
e.addToLoop()
e.setPriority(1)
class EventLoopSimpleTests(unittest.TestCase):
def testSimpleSocketCallback(self):
def serverCallback(fd, events, eventObj):
s = socket.fromfd(fd, socket.AF_INET, socket.SOCK_STREAM)
client, addr = s.accept()
client.send("foo")
eventObj.removeFromLoop()
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.setblocking(False)
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
s.bind(("127.0.0.1", 50505))
s.listen(5)
serverEvent = libevent.createEvent(fd=s, events=libevent.EV_READ, callback=serverCallback)
serverEvent.addToLoop()
c = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
c.setblocking(False)
c.connect_ex(("127.0.0.1", 50505))
libevent.dispatch()
c.setblocking(True)
self.assertEqual(c.recv(3), "foo")
c.close()
s.close()
def testSimpleTimerCallback(self):
t = int(time.time())
cb = lambda fd, events, obj: self.assertEqual(int(time.time())-t, 2)
timer = libevent.createTimer(cb)
timer.addToLoop(timeout=2)
libevent.loop(libevent.EVLOOP_ONCE)
def testLoopExit(self):
cb = lambda fd, events, obj: libevent.loopExit(0)
timer = libevent.createTimer(cb)
timer.addToLoop(timeout=2)
libevent.dispatch()
def testSignalHandler(self):
signalHandlerCallback = lambda signum, events, obj: obj.removeFromLoop()
signalHandler = libevent.createSignalHandler(signal.SIGUSR1, signalHandlerCallback)
signalHandler.addToLoop()
signalSenderCallback = lambda fd, events, obj: os.kill(os.getpid(), signal.SIGUSR1)
timer = libevent.createTimer(signalSenderCallback)
timer.addToLoop(1)
libevent.dispatch()
# if we get here, it worked - suboptimal way to test this
if __name__=='__main__':
unittest.main()

View File

@ -0,0 +1,20 @@
import unittest
import libevent
__all__ = ["EventBaseTests"]
class EventBaseTests(unittest.TestCase):
def testEventBaseValidConstructionNoArgs(self):
eventBase = libevent.EventBase()
def testEventBaseValidConstructionOneArg(self):
eventBase = libevent.EventBase(3)
def testEventBaseValidConstructionKwargs(self):
eventBase = libevent.EventBase(numPriorities=3)
def testEventBaseInvalidConstruction(self):
self.assertRaises(TypeError, libevent.EventBase, stupid=1)
if __name__=='__main__':
unittest.main()

View File

@ -0,0 +1,10 @@
import unittest
import libevent
__all__ = ["PackageTests"]
class PackageTests(unittest.TestCase):
pass
if __name__=='__main__':
unittest.main()

View File

View File

@ -0,0 +1,18 @@
Metadata-Version: 1.0
Name: libevent-python
Version: 0.1a8
Summary: A CPython extension module wrapping the libevent library
Home-page: http://python-hpio.net/trac/wiki/LibEventPython
Author: Andy Gross
Author-email: andy@andygross.org
License: BSD
Description: UNKNOWN
Platform: UNKNOWN
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: BSD License
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Classifier: Topic :: System :: Networking
Classifier: Topic :: Internet

View File

@ -0,0 +1,18 @@
INSTALL.txt
LICENSE.txt
README.txt
TODO.txt
ez_setup.py
setup.py
examples/echo_server.py
libevent/__init__.py
libevent/eventmodule.c
libevent/tests/TestAll.py
libevent/tests/TestEvent.py
libevent/tests/TestEventBase.py
libevent/tests/TestPackage.py
libevent/tests/__init__.py
libevent_python.egg-info/PKG-INFO
libevent_python.egg-info/SOURCES.txt
libevent_python.egg-info/not-zip-safe
libevent_python.egg-info/top_level.txt

View File

View File

@ -0,0 +1 @@
libevent

42
setup.py Normal file
View File

@ -0,0 +1,42 @@
#!/usr/bin/python
# Copyright (c) 2006 Andy Gross <andy@andygross.org>
# Copyright (c) 2006 Nick Mathewson
# See LICENSE.txt for details.
import os, sys, ez_setup
ez_setup.use_setuptools()
from setuptools import setup, Extension, find_packages
extensions = [
Extension("libevent.event",
["libevent/eventmodule.c"],
include_dirs=["/usr/local/include"],
library_dirs=["/usr/local/lib"],
libraries=["event"]),
]
setup(
name="libevent-python",
version="0.1a8",
description="A CPython extension module wrapping the libevent library",
author="Andy Gross",
author_email="andy@andygross.org",
url="http://python-hpio.net/trac/wiki/LibEventPython",
license="BSD",
packages=find_packages(),
package_data={'': ['*.txt', 'ez_setup.py', 'examples/*']},
ext_modules = extensions,
zip_safe = False,
test_suite = "libevent.tests.TestAll",
classifiers = [f.strip() for f in """
Development Status :: 3 - Alpha
Intended Audience :: Developers
License :: OSI Approved :: BSD License
Operating System :: OS Independent
Programming Language :: Python
Topic :: Software Development :: Libraries :: Python Modules
Topic :: System :: Networking
Topic :: Internet""".splitlines() if f.strip()],
)