import libevent-python-0.1a8
This commit is contained in:
commit
5bfa1dc607
|
@ -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
|
||||
|
|
@ -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.
|
|
@ -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
|
|
@ -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.
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
* More documentation
|
||||
* Buffered events
|
||||
* Twisted integration
|
||||
* More examples
|
||||
* Support for libevent-CVS features
|
|
@ -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())
|
||||
|
|
@ -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:])
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
@ -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()
|
|
@ -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()));
|
||||
}
|
|
@ -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()
|
|
@ -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()
|
|
@ -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()
|
|
@ -0,0 +1,10 @@
|
|||
import unittest
|
||||
import libevent
|
||||
|
||||
__all__ = ["PackageTests"]
|
||||
|
||||
class PackageTests(unittest.TestCase):
|
||||
pass
|
||||
|
||||
if __name__=='__main__':
|
||||
unittest.main()
|
|
@ -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
|
|
@ -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
|
|
@ -0,0 +1 @@
|
|||
libevent
|
|
@ -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()],
|
||||
)
|
||||
|
Loading…
Reference in New Issue