Commit 6c7b8ee8 authored by Denis Bilenko's avatar Denis Bilenko

major backward-incompatible changes to core; it is now libevent-2 based

- libevent1.4 currently does not compile
- interface is not compatible with previous version
- core no longer initializes the event base (Hub does it)
- core.event and core.http accept event_base
- core.event clears 'callback' and 'arg' after callback is executed or it was cancelled (unless it was persistent). this helps to avoid creating GC cycles
- core: read_event/write_event/timer/active_event/.. are gone (but now available as methods on event_base, with different interface, however)
parent 29aceb20
...@@ -44,7 +44,6 @@ core.EV_WRITE = 0x04 ...@@ -44,7 +44,6 @@ core.EV_WRITE = 0x04
core.EV_SIGNAL = 0x08 core.EV_SIGNAL = 0x08
core.EV_PERSIST = 0x10 core.EV_PERSIST = 0x10
from gevent.core import reinit
from gevent.greenlet import Greenlet, joinall, killall from gevent.greenlet import Greenlet, joinall, killall
spawn = Greenlet.spawn spawn = Greenlet.spawn
spawn_later = Greenlet.spawn_later spawn_later = Greenlet.spawn_later
...@@ -57,3 +56,8 @@ try: ...@@ -57,3 +56,8 @@ try:
from gevent.hub import fork from gevent.hub import fork
except ImportError: except ImportError:
__all__.remove('fork') __all__.remove('fork')
def reinit():
from gevent.hub import get_hub
return get_hub().reinit()
# # Copyright (c) 2009-2010 Denis Bilenko and gevent contributors. See LICENSE for details.
# event.pyx
#
# libevent Python bindings
#
# Copyright (c) 2004 Dug Song <dugsong@monkey.org>
# Copyright (c) 2003 Martin Murray <murrayma@citi.umich.edu>
# Copyright (c) 2009-2010 Denis Bilenko <denis.bilenko@gmail.com>
#
"""Wrappers around libevent API. """Wrappers around libevent API.
...@@ -15,27 +7,20 @@ specific event on a file handle, file descriptor, or signal occurs, ...@@ -15,27 +7,20 @@ specific event on a file handle, file descriptor, or signal occurs,
or after a given time has passed. It also provides wrappers around or after a given time has passed. It also provides wrappers around
structures and functions from libevent-dns and libevent-http. structures and functions from libevent-dns and libevent-http.
This module does not work with the greenlets. A callback passed This module does not work with greenlets. A callback passed
to a method from this module will be executed in the event loop, to a method from this module will be executed in the event loop,
which is running in the :class:`Hub <gevent.hub.Hub>` greenlet. which is running in the :class:`Hub <gevent.hub.Hub>` greenlet.
Therefore it must not use any synchronous gevent API, Therefore it must not use any synchronous gevent API, that is, blocking
that is, the functions that switch to the Hub. It's OK to call asynchronous functions that need to switch to the Hub. It's OK to call asynchronous
stuff like :func:`gevent.spawn`, :meth:`Event.set <gevent.event.Event.set` or stuff like :func:`gevent.spawn`, :meth:`Event.set <gevent.event.Event.set`
:meth:`Queue.put_nowait <gevent.queue.Queue.put_nowait>`. or :meth:`Queue.put_nowait <gevent.queue.Queue.put_nowait>`.
The code is based on pyevent_. This module is very similar to pyevent_. In fact, it grew out of pyevent_ source code.
However it is currently more up to date with regard to libevent API.
.. _pyevent: http://code.google.com/p/pyevent/ .. _pyevent: http://code.google.com/p/pyevent/
""" """
__author__ = ( 'Dug Song <dugsong@monkey.org>',
'Martin Murray <mmurray@monkey.org>' )
__copyright__ = ( 'Copyright (c) 2004 Dug Song',
'Copyright (c) 2003 Martin Murray' )
__license__ = 'BSD'
__url__ = 'http://monkey.org/~dugsong/pyevent/'
__version__ = '0.4+'
__all__ = ['event', 'read_event', 'write_event', 'timer', 'signal', 'active_event', __all__ = ['event', 'read_event', 'write_event', 'timer', 'signal', 'active_event',
'init', 'dispatch', 'loop', 'get_version', 'get_method', 'get_header_version'] 'init', 'dispatch', 'loop', 'get_version', 'get_method', 'get_header_version']
# note, that .pxi files append stuff to __all__ # note, that .pxi files append stuff to __all__
...@@ -45,6 +30,11 @@ import traceback ...@@ -45,6 +30,11 @@ import traceback
from pprint import pformat from pprint import pformat
import weakref import weakref
cimport levent
import _socket
gaierror = _socket.gaierror
cdef extern from "sys/types.h": cdef extern from "sys/types.h":
ctypedef unsigned char u_char ctypedef unsigned char u_char
...@@ -55,62 +45,24 @@ cdef extern from "Python.h": ...@@ -55,62 +45,24 @@ cdef extern from "Python.h":
object PyString_FromStringAndSize(char *v, int len) object PyString_FromStringAndSize(char *v, int len)
object PyString_FromString(char *v) object PyString_FromString(char *v)
int PyObject_AsCharBuffer(object obj, char **buffer, int *buffer_len) int PyObject_AsCharBuffer(object obj, char **buffer, int *buffer_len)
void PyOS_snprintf(void*, size_t, char*, ...)
cdef extern from "frameobject.h": cdef extern from "frameobject.h":
ctypedef struct PyThreadState: ctypedef struct PyThreadState:
void* exc_type void* exc_type
void* exc_value void* exc_value
void* exc_traceback void* exc_traceback
PyThreadState* PyThreadState_GET() PyThreadState* PyThreadState_GET()
ctypedef void (*event_handler)(int fd, short evtype, void *arg) cdef extern from "stdio.h":
void* memset(void*, int, size_t)
ctypedef void* event_base cdef extern from "socketmodule.h":
char* get_gaierror(int)
cdef extern from "libevent.h": object makesockaddr(int sockfd, void *, int addrlen, int proto)
# event.h:
struct timeval:
unsigned int tv_sec
unsigned int tv_usec
struct event_t "event":
int ev_fd
short ev_events
int ev_flags
void *ev_arg
void* event_init()
int event_reinit(void *base)
char* event_get_version()
char* event_get_method()
void event_set(event_t *ev, int fd, short event, event_handler handler, void *arg)
void evtimer_set(event_t *ev, event_handler handler, void *arg)
int event_add(event_t *ev, timeval *tv)
int event_del(event_t *ev)
int event_dispatch() nogil
int event_loop(int loop) nogil
int event_pending(event_t *ev, short, timeval *tv)
void event_active(event_t *ev, int res, short ncalls)
int EVLOOP_ONCE
int EVLOOP_NONBLOCK
char* _EVENT_VERSION
int EV_TIMEOUT
int EV_READ
int EV_WRITE
int EV_SIGNAL
int EV_PERSIST
int EVLIST_TIMEOUT
int EVLIST_INSERTED
int EVLIST_SIGNAL
int EVLIST_ACTIVE
int EVLIST_INTERNAL
int EVLIST_INIT
ctypedef void (*event_handler)(int fd, short evtype, void *arg)
ctypedef void (*event_log_cb)(int severity, char *msg)
cdef extern from "string.h": cdef extern from "string.h":
char* strerror(int errnum) char* strerror(int errnum)
...@@ -119,8 +71,291 @@ cdef extern from "errno.h": ...@@ -119,8 +71,291 @@ cdef extern from "errno.h":
int errno int errno
cdef extern from "libevent.h": cdef class event_base:
event_base* current_base
cdef void* _ptr
cdef object _dns
def __init__(self, size_t ptr=0):
if ptr:
self._ptr = <void*>ptr
else:
self._ptr = levent.event_base_new()
if not self._ptr:
if errno:
raise IOError(errno, strerror(errno))
else:
raise IOError("event_base_new returned NULL")
def __dealloc__(self):
self.free()
property ptr:
def __get__(self):
return <size_t>self._ptr
property dns:
def __get__(self):
if self._dns is None:
self._dns = evdns_base.new(self)
return self._dns
def dispatch(self):
return levent.event_base_dispatch(self._ptr)
def reinit(self):
cdef int result = levent.event_reinit(self._ptr)
if result != 0:
raise IOError('event_reinit failed with %s' % result)
def get_method(self):
return levent.event_base_get_method(self._ptr)
def get_info(self):
return 'libevent-%s/%s' % (get_version(), self.get_method())
def free(self):
if self._ptr:
levent.event_base_free(self._ptr)
self._ptr = NULL
if self._dns is not None:
self._dns.free()
self._dns = None
def read_event(self, int handle, persist=False):
cdef short evtype = levent.EV_READ
if persist:
evtype = evtype | levent.EV_PERSIST
cdef event ev = event(evtype, handle, self)
return ev
def write_event(self, int handle, persist=False):
cdef short evtype = levent.EV_WRITE
if persist:
evtype = evtype | levent.EV_PERSIST
cdef event ev = event(evtype, handle, base=self)
return ev
def readwrite_event(self, int handle, persist=False):
cdef short evtype = levent.EV_READ | levent.EV_WRITE
if persist:
evtype = evtype | levent.EV_PERSIST
cdef event ev = event(evtype, handle, base=self)
return ev
def signal(self, int signalnum, callback, *args, **kwargs):
cdef event ev = simple_event(levent.EV_SIGNAL|levent.EV_PERSIST, signalnum, base=self)
ev.add(None, callback, *args, **kwargs)
return ev
def timer(self, seconds=None, callback=None, *args, **kwargs):
cdef event ev = simple_event(0, -1, base=self)
if callback is not None:
ev.add(seconds, callback, *args, **kwargs)
return ev
def active_event(self, callback, *args, **kwargs):
cdef event ev = simple_event(0, -1, base=self)
ev.active(callback, *args, **kwargs)
return ev
cdef class evdns_base:
cdef void* _ptr
cdef public event_base base
def __init__(self, event_base base, size_t ptr=0):
self.base = base
self._ptr = <void*>ptr
@classmethod
def new(cls, object base, int init=1):
cdef void* ptr = levent.evdns_base_new((<event_base?>base)._ptr, <int>init)
if not ptr:
if errno:
raise IOError(errno, strerror(errno))
else:
raise IOError("evdns_base_new returned NULL")
return cls(base, <size_t>ptr)
property ptr:
def __get__(self):
return <size_t>self._ptr
def free(self, int fail_requests=1):
if self._ptr:
self.base = None
levent.evdns_base_free(self._ptr, fail_requests)
self._ptr = NULL
def add_nameserver(self, char* address):
"""Add a nameserver.
This function parses a n IPv4 or IPv6 address from a string and adds it as a
nameserver. It supports the following formats:
- [IPv6Address]:port
- [IPv6Address]
- IPv6Address
- IPv4Address:port
- IPv4Address
If no port is specified, it defaults to 53."""
cdef int result = levent.evdns_base_nameserver_ip_add(self._ptr, address)
if result:
if errno:
raise IOError(errno, strerror(errno))
else:
raise IOError("evdns_base_nameserver_ip_add returned %r" % result)
def count_nameservers(self):
return levent.evdns_base_count_nameservers(self._ptr)
def set_option(self, char* option, object val):
"""Set the value of a configuration option.
The available configuration options are (as of libevent-2.0.8-rc):
ndots, timeout, max-timeouts, max-inflight, attempts, randomize-case,
bind-to, initial-probe-timeout, getaddrinfo-allow-skew."""
# XXX auto add colon fix it: In versions before Libevent 2.0.3-alpha, the option name needed to end with a colon.
cdef char* c_val
if isinstance(val, str):
c_val = val
elif isinstance(val, (int, long, float)):
val = str(val)
c_val = val
elif isinstance(val, unicode):
val = val.encode('ascii')
c_val = val
else:
raise TypeError('Expected a string or a number: %r' % (val, ))
cdef int result = levent.evdns_base_set_option(self._ptr, option, c_val)
if result:
if errno:
raise IOError(errno, strerror(errno))
else:
raise IOError("evdns_base_set_option returned %r" % result)
def getaddrinfo(self, callback, host, port, int family=0, int socktype=0, int proto=0, int flags=0, arg=None):
# evdns and socket module do not match flags
cdef char* nodename = NULL
cdef char* servname = NULL
cdef char pbuf[30]
cdef levent.evutil_addrinfo hints
if host is None:
pass
elif isinstance(host, unicode):
host = host.encode('idna')
nodename = host
elif isinstance(host, str):
nodename = host
else:
raise TypeError("getaddrinfo() first argument must be string or None")
if port is None:
pass
elif isinstance(port, (int, long)):
PyOS_snprintf(pbuf, sizeof(pbuf), "%ld", <long>port)
servname = pbuf
else:
servname = <char*?>port # check that it raises TypeError
memset(&hints, 0, sizeof(hints))
hints.ai_family = family
hints.ai_socktype = socktype
hints.ai_protocol = proto
hints.ai_flags = flags
cdef object param = [callback, arg]
Py_INCREF(param)
cdef void* c_request = levent.evdns_getaddrinfo(self._ptr, nodename, servname, &hints, __getaddrinfo_handler, <void*>param)
if c_request:
request = getaddrinfo_request(self.base, <size_t>c_request)
param.append(request)
return request
cdef void __getaddrinfo_handler(int code, levent.evutil_addrinfo* res, void* c_param):
cdef object callback
cdef object arg
cdef object request = None
cdef object param = <object>c_param
Py_DECREF(param)
cdef list result
cdef char* canonname
try:
if len(param) == 2:
callback, arg = param
else:
callback, arg, request = param
if request is not None:
if request.base is None: # cancelled?
request.detach()
return
request.detach()
if code:
callback(None, gaierror(code, get_gaierror(code)), arg)
else:
result = []
while res:
if res.ai_canonname:
canonname = res.ai_canonname
else:
canonname = ''
result.append((res.ai_family,
res.ai_socktype,
res.ai_protocol,
canonname,
makesockaddr(-1, res.ai_addr, res.ai_addrlen, res.ai_protocol)))
res = res.ai_next
callback(result, None, arg)
except:
traceback.print_exc()
try:
sys.stderr.write('Failed to execute callback %r\n\n' % (callback, ))
except:
traceback.print_exc()
sys.exc_clear()
cdef class getaddrinfo_request:
cdef public event_base base
cdef void* _ptr
def __init__(self, event_base base, size_t ptr=0):
self.base = base
self._ptr = <void*>ptr
property ptr:
def __get__(self):
return <size_t>self._ptr
def __str__(self):
return '<%s ptr=%x>' % (self.__class__.__name__, self.ptr)
def detach(self):
self.base = None
self._ptr = NULL
def _cancel(self):
if self._ptr and self.base is not None:
levent.evdns_getaddrinfo_cancel(self._ptr)
# getaddrinfo_handler will be called immediatelly, with EVUTIL_EAI_CANCEL argument
def cancel(self):
if self._ptr and self.base is not None:
self.base.active_event(self._cancel)
self.base = None
@property
def pending(self):
if self._ptr:
return self.base is not None
cdef void __event_handler(int fd, short evtype, void *arg) with gil: cdef void __event_handler(int fd, short evtype, void *arg) with gil:
...@@ -135,7 +370,7 @@ cdef void __event_handler(int fd, short evtype, void *arg) with gil: ...@@ -135,7 +370,7 @@ cdef void __event_handler(int fd, short evtype, void *arg) with gil:
traceback.print_exc() traceback.print_exc()
sys.exc_clear() sys.exc_clear()
finally: finally:
if not event_pending(&self.ev, EV_READ|EV_WRITE|EV_SIGNAL|EV_TIMEOUT, NULL): if not levent.event_pending(&self.ev, levent.EV_READ|levent.EV_WRITE|levent.EV_SIGNAL|levent.EV_TIMEOUT, NULL):
self._delref() self._delref()
...@@ -147,20 +382,17 @@ cdef class event: ...@@ -147,20 +382,17 @@ cdef class event:
- *callback* -- user callback with ``(event, evtype)`` prototype - *callback* -- user callback with ``(event, evtype)`` prototype
- *arg* -- optional object, which will be made available as :attr:`arg` property. - *arg* -- optional object, which will be made available as :attr:`arg` property.
""" """
cdef event_t ev cdef levent.event ev
cdef public object callback cdef public object callback
cdef public object arg cdef public object arg
cdef int _incref # 1 if we already INCREFed this object once (because libevent references it) cdef int _incref # 1 if we already INCREFed this object once (because libevent references it)
def __init__(self, short evtype, int handle, callback, arg=None): def __init__(self, short evtype, int handle, event_base base=None):
self.callback = callback
self.arg = arg
self._incref = 0 self._incref = 0
cdef void* c_self = <void*>self cdef void* c_self = <void*>self
if evtype == 0 and not handle: levent.event_set(&self.ev, handle, evtype, __event_handler, c_self)
evtimer_set(&self.ev, __event_handler, c_self) if base is not None:
else: levent.event_base_set((<event_base?>base)._ptr, &self.ev)
event_set(&self.ev, handle, evtype, __event_handler, c_self)
cdef _addref(self): cdef _addref(self):
if self._incref <= 0: if self._incref <= 0:
...@@ -171,12 +403,14 @@ cdef class event: ...@@ -171,12 +403,14 @@ cdef class event:
if self._incref > 0: if self._incref > 0:
Py_DECREF(self) Py_DECREF(self)
self._incref -= 1 self._incref -= 1
self.callback = None
self.arg = None
property pending: property pending:
"""Return True if the event is still scheduled to run.""" """Return True if the event is still scheduled to run."""
def __get__(self): def __get__(self):
return event_pending(&self.ev, EV_TIMEOUT|EV_SIGNAL|EV_READ|EV_WRITE, NULL) return levent.event_pending(&self.ev, levent.EV_TIMEOUT|levent.EV_SIGNAL|levent.EV_READ|levent.EV_WRITE, NULL)
property fd: property fd:
...@@ -194,8 +428,11 @@ cdef class event: ...@@ -194,8 +428,11 @@ cdef class event:
result = [] result = []
cdef short events = self.ev.ev_events cdef short events = self.ev.ev_events
cdef short c_event cdef short c_event
for (event, txt) in ((EV_TIMEOUT, 'TIMEOUT'), (EV_READ, 'READ'), (EV_WRITE, 'WRITE'), for (event, txt) in ((levent.EV_TIMEOUT, 'TIMEOUT'),
(EV_SIGNAL, 'SIGNAL'), (EV_PERSIST, 'PERSIST')): (levent.EV_READ, 'READ'),
(levent.EV_WRITE, 'WRITE'),
(levent.EV_SIGNAL, 'SIGNAL'),
(levent.EV_PERSIST, 'PERSIST')):
c_event = event c_event = event
if events & c_event: if events & c_event:
result.append(txt) result.append(txt)
...@@ -216,8 +453,12 @@ cdef class event: ...@@ -216,8 +453,12 @@ cdef class event:
result = [] result = []
cdef int flags = self.ev.ev_flags cdef int flags = self.ev.ev_flags
cdef int c_flag cdef int c_flag
for (flag, txt) in ((EVLIST_TIMEOUT, 'TIMEOUT'), (EVLIST_INSERTED, 'INSERTED'), (EVLIST_SIGNAL, 'SIGNAL'), for (flag, txt) in ((levent.EVLIST_TIMEOUT, 'TIMEOUT'),
(EVLIST_ACTIVE, 'ACTIVE'), (EVLIST_INTERNAL, 'INTERNAL'), (EVLIST_INIT, 'INIT')): (levent.EVLIST_INSERTED, 'INSERTED'),
(levent.EVLIST_SIGNAL, 'SIGNAL'),
(levent.EVLIST_ACTIVE, 'ACTIVE'),
(levent.EVLIST_INTERNAL, 'INTERNAL'),
(levent.EVLIST_INIT, 'INIT')):
c_flag = flag c_flag = flag
if flags & c_flag: if flags & c_flag:
result.append(txt) result.append(txt)
...@@ -227,16 +468,15 @@ cdef class event: ...@@ -227,16 +468,15 @@ cdef class event:
result.append(hex(flags)) result.append(hex(flags))
return '|'.join(result) return '|'.join(result)
def add(self, timeout=None): def add(self, timeout, callback, arg=None):
"""Add event to be executed after an optional *timeout* - number of seconds cdef levent.timeval tv
after which the event will be executed."""
global errno
cdef timeval tv
cdef double c_timeout cdef double c_timeout
cdef int result cdef int result
errno = 0 # event_add sometime does not set errno if not self.ev.ev_base:
# libevent starting with 2.0.7 actually does check for this condition, so we should not
raise AssertionError('ev_base is not set')
if timeout is None: if timeout is None:
result = event_add(&self.ev, NULL) result = levent.event_add(&self.ev, NULL)
else: else:
c_timeout = <double>timeout c_timeout = <double>timeout
if c_timeout < 0.0: if c_timeout < 0.0:
...@@ -244,19 +484,30 @@ cdef class event: ...@@ -244,19 +484,30 @@ cdef class event:
else: else:
tv.tv_sec = <long>c_timeout tv.tv_sec = <long>c_timeout
tv.tv_usec = <unsigned int>((c_timeout - <double>tv.tv_sec) * 1000000.0) tv.tv_usec = <unsigned int>((c_timeout - <double>tv.tv_sec) * 1000000.0)
result = event_add(&self.ev, &tv) result = levent.event_add(&self.ev, &tv)
if result < 0: if result < 0:
if errno: if errno:
raise IOError(errno, strerror(errno)) raise IOError(errno, strerror(errno))
else: else:
raise IOError("event_add(fileno=%s) returned %s" % (self.fd, result)) raise IOError("event_add(fileno=%s) returned %s" % (self.fd, result))
self.callback = callback
self.arg = arg
self._addref()
def active(self, callback, arg):
levent.event_active(&self.ev, levent.EV_TIMEOUT, 1)
self.callback = callback
self.arg = arg
self._addref() self._addref()
def cancel(self): def cancel(self):
"""Remove event from the event queue.""" """Remove event from the event queue."""
cdef int result cdef int result
if event_pending(&self.ev, EV_TIMEOUT|EV_SIGNAL|EV_READ|EV_WRITE, NULL): # setting self.callback and self.arg to None to avoid refcounting cycles
result = event_del(&self.ev) self.callback = None
self.arg = None
if levent.event_pending(&self.ev, levent.EV_TIMEOUT|levent.EV_SIGNAL|levent.EV_READ|levent.EV_WRITE, NULL):
result = levent.event_del(&self.ev)
if result < 0: if result < 0:
return result return result
self._delref() self._delref()
...@@ -295,39 +546,6 @@ cdef class event: ...@@ -295,39 +546,6 @@ cdef class event:
self.cancel() self.cancel()
cdef class read_event(event):
"""Create a new scheduled event with evtype=EV_READ"""
def __init__(self, int handle, callback, timeout=None, arg=None, persist=False):
cdef short evtype = EV_READ
if persist:
evtype = evtype | EV_PERSIST
event.__init__(self, evtype, handle, callback, arg)
self.add(timeout)
cdef class write_event(event):
"""Create a new scheduled event with evtype=EV_WRITE"""
def __init__(self, int handle, callback, timeout=None, arg=None, persist=False):
cdef short evtype = EV_WRITE
if persist:
evtype = evtype | EV_PERSIST
event.__init__(self, evtype, handle, callback, arg)
self.add(timeout)
class readwrite_event(event):
"""Create a new scheduled event with evtype=EV_READ|EV_WRITE"""
def __init__(self, int handle, callback, timeout=None, arg=None, persist=False):
cdef short evtype = EV_READ|EV_WRITE
if persist:
evtype = evtype | EV_PERSIST
event.__init__(self, evtype, handle, callback, arg)
self.add(timeout)
cdef void __simple_handler(int fd, short evtype, void *arg) with gil: cdef void __simple_handler(int fd, short evtype, void *arg) with gil:
cdef event self = <event>arg cdef event self = <event>arg
try: try:
...@@ -341,125 +559,35 @@ cdef void __simple_handler(int fd, short evtype, void *arg) with gil: ...@@ -341,125 +559,35 @@ cdef void __simple_handler(int fd, short evtype, void *arg) with gil:
traceback.print_exc() traceback.print_exc()
sys.exc_clear() sys.exc_clear()
finally: finally:
if not event_pending(&self.ev, EV_READ|EV_WRITE|EV_SIGNAL|EV_TIMEOUT, NULL): if not levent.event_pending(&self.ev, levent.EV_READ|levent.EV_WRITE|levent.EV_SIGNAL|levent.EV_TIMEOUT, NULL):
self._delref() self._delref()
cdef class timer(event): cdef class simple_event(event):
"""Create a new scheduled timer"""
def __init__(self, float seconds, callback, *args, **kwargs):
self.callback = callback
self.arg = (args, kwargs)
evtimer_set(&self.ev, __simple_handler, <void*>self)
self.add(seconds)
cdef class signal(event):
"""Create a new persistent signal event"""
def __init__(self, int signalnum, callback, *args, **kwargs):
self.callback = callback
self.arg = (args, kwargs)
event_set(&self.ev, signalnum, EV_SIGNAL|EV_PERSIST, __simple_handler, <void*>self)
self.add()
cdef class active_event(event):
"""An event that is scheduled to run in the current loop iteration"""
def __init__(self, callback, *args, **kwargs):
self.callback = callback
self.arg = (args, kwargs)
evtimer_set(&self.ev, __simple_handler, <void*>self)
self._addref()
event_active(&self.ev, EV_TIMEOUT, 1)
def add(self, timeout=None):
raise NotImplementedError
def init():
"""Initialize event queue."""
event_init()
def __init__(self, short evtype, int handle, base=None):
self._incref = 0
cdef void* c_self = <void*>self
levent.event_set(&self.ev, handle, evtype, __simple_handler, c_self)
if base is not None:
levent.event_base_set((<event_base?>base)._ptr, &self.ev)
def dispatch(): def add(self, seconds, callback, *args, **kwargs):
"""Dispatch all events on the event queue. return event.add(self, seconds, callback, (args, kwargs))
Returns 0 on success, and 1 if no events are registered.
May raise IOError.
"""
cdef int ret
with nogil:
ret = event_dispatch()
if ret < 0:
raise IOError(errno, strerror(errno))
return ret
def loop(nonblock=False): def active(self, callback, *args, **kwargs):
"""Dispatch all pending events on queue in a single pass. return event.active(self, callback, (args, kwargs))
Returns 0 on success, and 1 if no events are registered.
May raise IOError.
"""
cdef int flags, ret
flags = EVLOOP_ONCE
if nonblock:
flags = EVLOOP_ONCE|EVLOOP_NONBLOCK
with nogil:
ret = event_loop(flags)
if ret < 0:
raise IOError(errno, strerror(errno))
return ret
def get_version(): def get_version():
"""Wrapper for :meth:`event_get_version`""" """Wrapper for :meth:`event_get_version`"""
return event_get_version() return levent.event_get_version()
def get_method():
"""Wrapper for :meth:`event_get_method`"""
return event_get_method()
cdef extern from *:
cdef void emit_ifdef "#if defined(_EVENT_VERSION) //" ()
cdef void emit_else "#else //" ()
cdef void emit_endif "#endif //" ()
# _EVENT_VERSION is available since libevent 1.4.0-beta
def get_header_version(): def get_header_version():
"""Return _EVENT_VERSION""" """Return _EVENT_VERSION"""
emit_ifdef() return levent._EVENT_VERSION
return _EVENT_VERSION
emit_endif()
# event_reinit is available since libevent 1.4.1-beta,
# but I cannot check for existence of a function here, can I?
# so I'm going to use _EVENT_VERSION as an indicator of event_reinit presence
# which will work in every version other than 1.4.0-beta
def reinit():
"""Wrapper for :meth:`event_reinit`."""
emit_ifdef()
return event_reinit(current_base)
emit_endif()
include "evdns.pxi"
# XXX - make sure event queue is always initialized.
init()
if get_version() != get_header_version() and get_header_version() is not None and get_version() != '1.3.99-trunk':
import warnings
msg = "libevent version mismatch: system version is %r but this gevent is compiled against %r" % (get_version(), get_header_version())
warnings.warn(msg, UserWarning, stacklevel=2)
include "evbuffer.pxi"
include "evhttp.pxi"
def set_exc_info(object typ, object value, object tb): def set_exc_info(object typ, object value, object tb):
cdef PyThreadState* tstate = PyThreadState_GET() cdef PyThreadState* tstate = PyThreadState_GET()
...@@ -476,3 +604,13 @@ def set_exc_info(object typ, object value, object tb): ...@@ -476,3 +604,13 @@ def set_exc_info(object typ, object value, object tb):
tstate.exc_value = <void *>value tstate.exc_value = <void *>value
tstate.exc_traceback = <void *>tb tstate.exc_traceback = <void *>tb
#include "evdns.pxi"
include "evbuffer.pxi"
include "evhttp.pxi"
if get_version() != get_header_version() and get_header_version() is not None and get_version() != '1.3.99-trunk':
import warnings
msg = "libevent version mismatch: system version is %r but this gevent is compiled against %r" % (get_version(), get_header_version())
warnings.warn(msg, UserWarning, stacklevel=2)
...@@ -3,7 +3,6 @@ ...@@ -3,7 +3,6 @@
import sys import sys
import traceback import traceback
from gevent.core import active_event
from gevent.hub import get_hub, getcurrent from gevent.hub import get_hub, getcurrent
from gevent.timeout import Timeout from gevent.timeout import Timeout
...@@ -35,7 +34,7 @@ class Semaphore(object): ...@@ -35,7 +34,7 @@ class Semaphore(object):
def release(self): def release(self):
self.counter += 1 self.counter += 1
if self._links and self.counter > 0 and self._notifier is None: if self._links and self.counter > 0 and self._notifier is None:
self._notifier = active_event(self._notify_links, list(self._links)) self._notifier = get_hub().reactor.active_event(self._notify_links, list(self._links))
def _notify_links(self, links): def _notify_links(self, links):
try: try:
...@@ -64,7 +63,7 @@ class Semaphore(object): ...@@ -64,7 +63,7 @@ class Semaphore(object):
raise TypeError('Expected callable: %r' % (callback, )) raise TypeError('Expected callable: %r' % (callback, ))
self._links.append(callback) self._links.append(callback)
if self.counter > 0 and self._notifier is None: if self.counter > 0 and self._notifier is None:
self._notifier = active_event(self._notify_links, list(self._links)) self._notifier = get_hub().reactor.active_event(self._notify_links, list(self._links))
def unlink(self, callback): def unlink(self, callback):
"""Remove the callback set by :meth:`rawlink`""" """Remove the callback set by :meth:`rawlink`"""
......
...@@ -20,10 +20,6 @@ __all__ = ['DNSError', ...@@ -20,10 +20,6 @@ __all__ = ['DNSError',
'QUERY_NO_SEARCH'] 'QUERY_NO_SEARCH']
# move from here into Hub.__init__ (once event_init() is move here as well)
core.dns_init()
class DNSError(gaierror): class DNSError(gaierror):
"""A subclass of :class:`socket.gaierror` used by :mod:`evdns` functions to report errors. """A subclass of :class:`socket.gaierror` used by :mod:`evdns` functions to report errors.
......
...@@ -3,7 +3,6 @@ ...@@ -3,7 +3,6 @@
import sys import sys
import traceback import traceback
from gevent import core
from gevent.hub import get_hub, getcurrent, _NONE from gevent.hub import get_hub, getcurrent, _NONE
from gevent.timeout import Timeout from gevent.timeout import Timeout
...@@ -40,7 +39,7 @@ class Event(object): ...@@ -40,7 +39,7 @@ class Event(object):
self._flag = True self._flag = True
if self._links: if self._links:
# schedule a job to notify the links already set # schedule a job to notify the links already set
core.active_event(self._notify_links, list(self._links)) get_hub().reactor.active_event(self._notify_links, list(self._links))
def clear(self): def clear(self):
"""Reset the internal flag to false. """Reset the internal flag to false.
...@@ -92,7 +91,7 @@ class Event(object): ...@@ -92,7 +91,7 @@ class Event(object):
raise TypeError('Expected callable: %r' % (callback, )) raise TypeError('Expected callable: %r' % (callback, ))
self._links.append(callback) self._links.append(callback)
if self._flag: if self._flag:
core.active_event(self._notify_links, list(self._links)) # XXX just pass [callback] get_hub().reactor.active_event(self._notify_links, list(self._links)) # XXX just pass [callback]
def unlink(self, callback): def unlink(self, callback):
"""Remove the callback set by :meth:`rawlink`""" """Remove the callback set by :meth:`rawlink`"""
...@@ -178,7 +177,7 @@ class AsyncResult(object): ...@@ -178,7 +177,7 @@ class AsyncResult(object):
self.value = value self.value = value
self._exception = None self._exception = None
if self._links and self._notifier is None: if self._links and self._notifier is None:
self._notifier = core.active_event(self._notify_links) self._notifier = get_hub().reactor.active_event(self._notify_links)
def set_exception(self, exception): def set_exception(self, exception):
"""Store the exception. Wake up the waiters. """Store the exception. Wake up the waiters.
...@@ -188,7 +187,7 @@ class AsyncResult(object): ...@@ -188,7 +187,7 @@ class AsyncResult(object):
""" """
self._exception = exception self._exception = exception
if self._links and self._notifier is None: if self._links and self._notifier is None:
self._notifier = core.active_event(self._notify_links) self._notifier = get_hub().reactor.active_event(self._notify_links)
def get(self, block=True, timeout=None): def get(self, block=True, timeout=None):
"""Return the stored value or raise the exception. """Return the stored value or raise the exception.
...@@ -293,7 +292,7 @@ class AsyncResult(object): ...@@ -293,7 +292,7 @@ class AsyncResult(object):
raise TypeError('Expected callable: %r' % (callback, )) raise TypeError('Expected callable: %r' % (callback, ))
self._links.add(callback) self._links.add(callback)
if self.ready() and self._notifier is None: if self.ready() and self._notifier is None:
self._notifier = core.active_event(self._notify_links) self._notifier = get_hub().reactor.active_event(self._notify_links)
def unlink(self, callback): def unlink(self, callback):
"""Remove the callback set by :meth:`rawlink`""" """Remove the callback set by :meth:`rawlink`"""
......
...@@ -54,7 +54,7 @@ cdef extern from "libevent.h": ...@@ -54,7 +54,7 @@ cdef extern from "libevent.h":
# evhttp # evhttp
ctypedef void (*evhttp_handler)(evhttp_request *, void *arg) ctypedef void (*evhttp_handler)(evhttp_request *, void *arg)
evhttp* evhttp_new(event_base *base) evhttp* evhttp_new(void *base)
int evhttp_bind_socket(evhttp *http, char* address, int port) int evhttp_bind_socket(evhttp *http, char* address, int port)
int evhttp_accept_socket(evhttp *http, int fd) int evhttp_accept_socket(evhttp *http, int fd)
void evhttp_free(evhttp* http) void evhttp_free(evhttp* http)
...@@ -511,14 +511,20 @@ cdef class http: ...@@ -511,14 +511,20 @@ cdef class http:
cdef public object default_response_headers cdef public object default_response_headers
cdef public dict _requests cdef public dict _requests
def __init__(self, object handle, object default_response_headers=None): def __init__(self, object handle, object default_response_headers=None, event_base base=None):
self.handle = handle self.handle = handle
if default_response_headers is None: if default_response_headers is None:
self.default_response_headers = [] self.default_response_headers = []
else: else:
self.default_response_headers = default_response_headers self.default_response_headers = default_response_headers
self._requests = {} # maps connection id to WeakKeyDictionary which holds requests self._requests = {} # maps connection id to WeakKeyDictionary which holds requests
self.__obj = evhttp_new(current_base) if base is None:
if levent.current_base:
self.__obj = evhttp_new(levent.current_base)
else:
raise ValueError('Please provide event_base')
else:
self.__obj = evhttp_new(base._ptr)
evhttp_set_gencb(self.__obj, _http_cb_handler, <void *>self) evhttp_set_gencb(self.__obj, _http_cb_handler, <void *>self)
def __dealloc__(self): def __dealloc__(self):
......
...@@ -2,7 +2,6 @@ ...@@ -2,7 +2,6 @@
import sys import sys
import traceback import traceback
from gevent import core
from gevent.hub import greenlet, getcurrent, get_hub, GreenletExit, Waiter, kill from gevent.hub import greenlet, getcurrent, get_hub, GreenletExit, Waiter, kill
from gevent.timeout import Timeout from gevent.timeout import Timeout
...@@ -242,12 +241,12 @@ class Greenlet(greenlet): ...@@ -242,12 +241,12 @@ class Greenlet(greenlet):
def start(self): def start(self):
"""Schedule the greenlet to run in this loop iteration""" """Schedule the greenlet to run in this loop iteration"""
assert not self.started, 'Greenlet already started' assert not self.started, 'Greenlet already started'
self._start_event = core.active_event(self.switch) self._start_event = get_hub().reactor.active_event(self.switch)
def start_later(self, seconds): def start_later(self, seconds):
"""Schedule the greenlet to run in the future loop iteration *seconds* later""" """Schedule the greenlet to run in the future loop iteration *seconds* later"""
assert not self.started, 'Greenlet already started' assert not self.started, 'Greenlet already started'
self._start_event = core.timer(seconds, self.switch) self._start_event = get_hub().reactor.timer(seconds, self.switch)
@classmethod @classmethod
def spawn(cls, *args, **kwargs): def spawn(cls, *args, **kwargs):
...@@ -302,7 +301,7 @@ class Greenlet(greenlet): ...@@ -302,7 +301,7 @@ class Greenlet(greenlet):
self._start_event = None self._start_event = None
if not self.dead: if not self.dead:
waiter = Waiter() waiter = Waiter()
core.active_event(_kill, self, exception, waiter) get_hub().reactor.active_event(_kill, self, exception, waiter)
if block: if block:
waiter.get() waiter.get()
self.join(timeout) self.join(timeout)
...@@ -375,7 +374,7 @@ class Greenlet(greenlet): ...@@ -375,7 +374,7 @@ class Greenlet(greenlet):
self._exception = None self._exception = None
self.value = result self.value = result
if self._links and self._notifier is None: if self._links and self._notifier is None:
self._notifier = core.active_event(self._notify_links) self._notifier = get_hub().reactor.active_event(self._notify_links)
def _report_error(self, exc_info): def _report_error(self, exc_info):
exception = exc_info[1] exception = exc_info[1]
...@@ -389,7 +388,7 @@ class Greenlet(greenlet): ...@@ -389,7 +388,7 @@ class Greenlet(greenlet):
self._exception = exception self._exception = exception
if self._links and self._notifier is None: if self._links and self._notifier is None:
self._notifier = core.active_event(self._notify_links) self._notifier = get_hub().reactor.active_event(self._notify_links)
info = str(self) + ' failed with ' info = str(self) + ' failed with '
try: try:
...@@ -421,7 +420,7 @@ class Greenlet(greenlet): ...@@ -421,7 +420,7 @@ class Greenlet(greenlet):
raise TypeError('Expected callable: %r' % (callback, )) raise TypeError('Expected callable: %r' % (callback, ))
self._links.add(callback) self._links.add(callback)
if self.ready() and self._notifier is None: if self.ready() and self._notifier is None:
self._notifier = core.active_event(self._notify_links) self._notifier = get_hub().reactor.active_event(self._notify_links)
def link(self, receiver=None, GreenletLink=GreenletLink, SpawnedLink=SpawnedLink): def link(self, receiver=None, GreenletLink=GreenletLink, SpawnedLink=SpawnedLink):
"""Link greenlet's completion to callable or another greenlet. """Link greenlet's completion to callable or another greenlet.
...@@ -546,7 +545,7 @@ def _killall(greenlets, exception): ...@@ -546,7 +545,7 @@ def _killall(greenlets, exception):
def killall(greenlets, exception=GreenletExit, block=True, timeout=None): def killall(greenlets, exception=GreenletExit, block=True, timeout=None):
if block: if block:
waiter = Waiter() waiter = Waiter()
core.active_event(_killall3, greenlets, exception, waiter) get_hub().reactor.active_event(_killall3, greenlets, exception, waiter)
if block: if block:
t = Timeout.start_new(timeout) t = Timeout.start_new(timeout)
try: try:
...@@ -556,7 +555,7 @@ def killall(greenlets, exception=GreenletExit, block=True, timeout=None): ...@@ -556,7 +555,7 @@ def killall(greenlets, exception=GreenletExit, block=True, timeout=None):
finally: finally:
t.cancel() t.cancel()
else: else:
core.active_event(_killall, greenlets, exception) get_hub().reactor.active_event(_killall, greenlets, exception)
class LinkedExited(Exception): class LinkedExited(Exception):
......
# Copyright (c) 2009-2010 Denis Bilenko. See LICENSE for details. # Copyright (c) 2009-2010 Denis Bilenko. See LICENSE for details.
from gevent import core from gevent import core
from gevent.baseserver import BaseServer from gevent.baseserver import BaseServer
from gevent.hub import get_hub
__all__ = ['HTTPServer'] __all__ = ['HTTPServer']
...@@ -38,7 +39,8 @@ class HTTPServer(BaseServer): ...@@ -38,7 +39,8 @@ class HTTPServer(BaseServer):
request.send_reply(503, 'Service Unavailable', msg) request.send_reply(503, 'Service Unavailable', msg)
def start_accepting(self): def start_accepting(self):
self.http = core.http(self._on_request, self.default_response_headers) reactor = get_hub().reactor
self.http = core.http(self._on_request, self.default_response_headers, base=reactor)
self.http.accept(self.socket.fileno()) self.http.accept(self.socket.fileno())
def stop_accepting(self): def stop_accepting(self):
......
...@@ -56,11 +56,11 @@ def _switch_helper(function, args, kwargs): ...@@ -56,11 +56,11 @@ def _switch_helper(function, args, kwargs):
def spawn_raw(function, *args, **kwargs): def spawn_raw(function, *args, **kwargs):
if kwargs: if kwargs:
g = greenlet(_switch_helper, get_hub()) g = greenlet(_switch_helper, get_hub())
core.active_event(g.switch, function, args, kwargs) get_hub().reactor.active_event(g.switch, function, args, kwargs)
return g return g
else: else:
g = greenlet(function, get_hub()) g = greenlet(function, get_hub())
core.active_event(g.switch, *args) get_hub().reactor.active_event(g.switch, *args)
return g return g
...@@ -74,7 +74,7 @@ def sleep(seconds=0): ...@@ -74,7 +74,7 @@ def sleep(seconds=0):
unique_mark = object() unique_mark = object()
if not seconds >= 0: if not seconds >= 0:
raise IOError(22, 'Invalid argument') raise IOError(22, 'Invalid argument')
timer = core.timer(seconds, getcurrent().switch, unique_mark) timer = get_hub().reactor.timer(seconds, getcurrent().switch, unique_mark)
try: try:
switch_result = get_hub().switch() switch_result = get_hub().switch()
assert switch_result is unique_mark, 'Invalid switch into sleep(): %r' % (switch_result, ) assert switch_result is unique_mark, 'Invalid switch into sleep(): %r' % (switch_result, )
...@@ -91,25 +91,25 @@ def kill(greenlet, exception=GreenletExit): ...@@ -91,25 +91,25 @@ def kill(greenlet, exception=GreenletExit):
so you have to use this function. so you have to use this function.
""" """
if not greenlet.dead: if not greenlet.dead:
core.active_event(greenlet.throw, exception) get_hub().reactor.active_event(greenlet.throw, exception)
def _wrap_signal_handler(handler, args, kwargs): def _wrap_signal_handler(handler, args, kwargs):
try: try:
handler(*args, **kwargs) handler(*args, **kwargs)
except: except:
core.active_event(MAIN.throw, *sys.exc_info()) get_hub().reactor.active_event(MAIN.throw, *sys.exc_info())
def signal(signalnum, handler, *args, **kwargs): def signal(signalnum, handler, *args, **kwargs):
return core.signal(signalnum, lambda: spawn_raw(_wrap_signal_handler, handler, args, kwargs)) return get_hub().reactor.signal(signalnum, lambda: spawn_raw(_wrap_signal_handler, handler, args, kwargs))
if _original_fork is not None: if _original_fork is not None:
def fork(): def fork():
result = _original_fork() result = _original_fork()
core.reinit() get_hub().reactor.reinit()
return result return result
...@@ -121,19 +121,31 @@ def shutdown(): ...@@ -121,19 +121,31 @@ def shutdown():
hub.shutdown() hub.shutdown()
def get_hub(): def get_hub_class():
"""Return the type of hub to use for the current thread.
If there's no type of hub for the current thread yet, 'gevent.hub.Hub' is used.
"""
global _threadlocal global _threadlocal
try:
return _threadlocal.hub
except AttributeError:
try: try:
hubtype = _threadlocal.Hub hubtype = _threadlocal.Hub
except AttributeError: except AttributeError:
# do not pretend to support multiple threads because it's not implemented properly by core.pyx hubtype = None
# this may change in the future, although currently I don't have a strong need for this
raise NotImplementedError('gevent is only usable from a single thread')
if hubtype is None: if hubtype is None:
hubtype = Hub hubtype = _threadlocal.Hub = Hub
return hubtype
def get_hub():
"""Return the hub for the current thread.
If hub does not exists in the current thread, the new one is created with call to :meth:`get_hub_class`.
"""
global _threadlocal
try:
return _threadlocal.hub
except AttributeError:
hubtype = get_hub_class()
hub = _threadlocal.hub = hubtype() hub = _threadlocal.hub = hubtype()
return hub return hub
...@@ -144,13 +156,19 @@ class Hub(greenlet): ...@@ -144,13 +156,19 @@ class Hub(greenlet):
It is created automatically by :func:`get_hub`. It is created automatically by :func:`get_hub`.
""" """
def __init__(self): reactor_class = core.event_base
def __init__(self, reactor=None):
greenlet.__init__(self) greenlet.__init__(self)
self.keyboard_interrupt_signal = None self.keyboard_interrupt_signal = None
if reactor is None:
self.reactor = self.reactor_class()
else:
self.reactor = reactor
def switch(self): def switch(self):
cur = getcurrent() cur = getcurrent()
assert cur is not self, 'Cannot switch to MAINLOOP from MAINLOOP' assert cur is not self, 'Impossible to call blocking function in the event loop callback'
exc_info = sys.exc_info() exc_info = sys.exc_info()
try: try:
sys.exc_clear() sys.exc_clear()
...@@ -166,16 +184,16 @@ class Hub(greenlet): ...@@ -166,16 +184,16 @@ class Hub(greenlet):
def run(self): def run(self):
global _threadlocal global _threadlocal
assert self is getcurrent(), 'Do not call run() directly' assert self is getcurrent(), 'Do not call Hub.run() directly'
try: try:
self.keyboard_interrupt_signal = signal(2, core.active_event, MAIN.throw, KeyboardInterrupt) self.keyboard_interrupt_signal = signal(2, self.reactor.active_event, MAIN.throw, KeyboardInterrupt)
except IOError: except IOError:
pass # no signal() on windows pass # no signal() on windows
try: try:
loop_count = 0 loop_count = 0
while True: while True:
try: try:
result = core.dispatch() result = self.reactor.dispatch()
except IOError, ex: except IOError, ex:
loop_count += 1 loop_count += 1
if loop_count > 15: if loop_count > 15:
...@@ -196,7 +214,9 @@ class Hub(greenlet): ...@@ -196,7 +214,9 @@ class Hub(greenlet):
if self.keyboard_interrupt_signal is not None: if self.keyboard_interrupt_signal is not None:
self.keyboard_interrupt_signal.cancel() self.keyboard_interrupt_signal.cancel()
self.keyboard_interrupt_signal = None self.keyboard_interrupt_signal = None
core.dns_shutdown() dns = self.reactor.dns
if dns is not None:
dns.free()
if not self or self.dead: if not self or self.dead:
if _threadlocal.__dict__.get('hub') is self: if _threadlocal.__dict__.get('hub') is self:
_threadlocal.__dict__.pop('hub') _threadlocal.__dict__.pop('hub')
...@@ -231,7 +251,7 @@ class Waiter(object): ...@@ -231,7 +251,7 @@ class Waiter(object):
The :meth:`get` method must be called from a greenlet other than :class:`Hub`. The :meth:`get` method must be called from a greenlet other than :class:`Hub`.
>>> result = Waiter() >>> result = Waiter()
>>> _ = core.timer(0.1, result.switch, 'hello from Waiter') >>> _ = get_hub().reactor.timer(0.1, result.switch, 'hello from Waiter')
>>> result.get() # blocks for 0.1 seconds >>> result.get() # blocks for 0.1 seconds
'hello from Waiter' 'hello from Waiter'
...@@ -239,7 +259,7 @@ class Waiter(object): ...@@ -239,7 +259,7 @@ class Waiter(object):
:class:`Waiter` stores the value. :class:`Waiter` stores the value.
>>> result = Waiter() >>> result = Waiter()
>>> _ = core.timer(0.1, result.switch, 'hi from Waiter') >>> _ = get_hub().reactor.timer(0.1, result.switch, 'hi from Waiter')
>>> sleep(0.2) >>> sleep(0.2)
>>> result.get() # returns immediatelly without blocking >>> result.get() # returns immediatelly without blocking
'hi from Waiter' 'hi from Waiter'
......
ctypedef void (*event_handler)(int fd, short evtype, void *arg)
ctypedef void (*event_log_cb)(int severity, char *msg)
cdef extern from "libevent.h":
# event.h:
struct timeval:
unsigned int tv_sec
unsigned int tv_usec
struct event:
void* ev_base
int ev_fd
short ev_events
int ev_flags
void *ev_arg
char* event_get_version()
int EV_TIMEOUT
int EV_READ
int EV_WRITE
int EV_SIGNAL
int EV_PERSIST
int EVLIST_TIMEOUT
int EVLIST_INSERTED
int EVLIST_SIGNAL
int EVLIST_ACTIVE
int EVLIST_INTERNAL
int EVLIST_INIT
int DNS_ERR_NONE
int DNS_ERR_FORMAT
int DNS_ERR_SERVERFAILED
int DNS_ERR_NOTEXIST
int DNS_ERR_NOTIMPL
int DNS_ERR_REFUSED
int DNS_ERR_TRUNCATED
int DNS_ERR_UNKNOWN
int DNS_ERR_TIMEOUT
int DNS_ERR_SHUTDOWN
int DNS_IPv4_A
int DNS_PTR
int DNS_IPv6_AAAA
int DNS_QUERY_NO_SEARCH
void* event_base_new()
int event_reinit(void *base)
int event_base_dispatch(void*) nogil
char* event_base_get_method(void*)
void event_base_free(void*)
int event_base_set(void *, event*)
void event_set(event *ev, int fd, short event, event_handler handler, void *arg)
int event_add(event *ev, timeval *tv)
int event_del(event *ev)
int event_pending(event *ev, short, timeval *tv)
void event_active(event *ev, int res, short ncalls)
int EVLOOP_ONCE
int EVLOOP_NONBLOCK
char* _EVENT_VERSION
struct evutil_addrinfo:
int ai_flags # AI_PASSIVE, AI_CANONNAME, AI_NUMERICHOST
int ai_family # PF_xxx
int ai_socktype # SOCK_xxx
int ai_protocol # 0 or IPPROTO_xxx for IPv4 and IPv6
size_t ai_addrlen # length of ai_addr
char *ai_canonname # canonical name for nodename
void *ai_addr # binary address
evutil_addrinfo *ai_next # next structure in linked list
ctypedef void (*evdns_callback_type)(int result, char t, int count, int ttl, void *addrs, void *arg)
ctypedef void (*evdns_getaddrinfo_cb)(int result, evutil_addrinfo *res, void *arg)
void* evdns_base_new(void *event_base, int initialize_nameservers)
void* current_base
void evdns_base_free(void *dns_base, int fail_requests)
char* evdns_err_to_string(int err)
int evdns_base_nameserver_ip_add(void *dns_base, char *ip_as_string)
int evdns_base_count_nameservers(void *base)
void *evdns_base_resolve_ipv4(void *dns_base, char *name, int flags, evdns_callback_type callback, void *ptr)
void *evdns_base_resolve_ipv6(void *dns_base, char *name, int flags, evdns_callback_type callback, void *ptr)
void *evdns_base_resolve_reverse(void *dns_base, void *, int flags, evdns_callback_type callback, void *ptr)
void *evdns_base_resolve_reverse_ipv6(void *dns_base, void *, int flags, evdns_callback_type callback, void *ptr)
void evdns_cancel_request(void *dns_base, void *req)
int evdns_base_set_option(void *dns_base, char *option, char *val)
void *evdns_getaddrinfo(void *dns_base, char *nodename, char *servname, evutil_addrinfo *hints_in, evdns_getaddrinfo_cb cb, void *arg)
void evdns_getaddrinfo_cancel(void*)
int EVUTIL_EAI_CANCEL
...@@ -20,6 +20,7 @@ ...@@ -20,6 +20,7 @@
#include "event2/buffer_compat.h" #include "event2/buffer_compat.h"
#include "event2/dns.h" #include "event2/dns.h"
#include "event2/dns_compat.h" #include "event2/dns_compat.h"
#include "event2/util.h"
#define EVBUFFER_DRAIN evbuffer_drain #define EVBUFFER_DRAIN evbuffer_drain
#define EVHTTP_SET_CB evhttp_set_cb #define EVHTTP_SET_CB evhttp_set_cb
......
...@@ -34,7 +34,6 @@ from Queue import Full, Empty ...@@ -34,7 +34,6 @@ from Queue import Full, Empty
from gevent.timeout import Timeout from gevent.timeout import Timeout
from gevent.hub import get_hub, Waiter, getcurrent, _NONE from gevent.hub import get_hub, Waiter, getcurrent, _NONE
from gevent import core
__all__ = ['Queue', 'PriorityQueue', 'LifoQueue', 'JoinableQueue'] __all__ = ['Queue', 'PriorityQueue', 'LifoQueue', 'JoinableQueue']
...@@ -240,7 +239,7 @@ class Queue(object): ...@@ -240,7 +239,7 @@ class Queue(object):
def _schedule_unlock(self): def _schedule_unlock(self):
if self._event_unlock is None: if self._event_unlock is None:
self._event_unlock = core.active_event(self._unlock) self._event_unlock = get_hub().reactor.active_event(self._unlock)
# QQQ re-activate event (with event_active libevent call) instead of creating a new one each time # QQQ re-activate event (with event_active libevent call) instead of creating a new one each time
def __iter__(self): def __iter__(self):
......
# Copyright (c) 2009-2010 Denis Bilenko. See LICENSE for details. # Copyright (c) 2009-2010 Denis Bilenko. See LICENSE for details.
from gevent import core
from gevent.timeout import Timeout from gevent.timeout import Timeout
from gevent.event import Event from gevent.event import Event
from gevent import core
from gevent.hub import get_hub
__implements__ = ['select'] __implements__ = ['select']
__all__ = ['error'] + __implements__ __all__ = ['error'] + __implements__
...@@ -36,12 +37,12 @@ class SelectResult(object): ...@@ -36,12 +37,12 @@ class SelectResult(object):
if evtype & core.EV_READ: if evtype & core.EV_READ:
self.read.append(event.arg) self.read.append(event.arg)
if self.timer is None: if self.timer is None:
self.timer = core.timer(0, self.event.set) self.timer = get_hub().reactor.timer(0, self.event.set)
elif evtype & core.EV_WRITE: elif evtype & core.EV_WRITE:
self.write.append(event.arg) self.write.append(event.arg)
if self.timer is None: if self.timer is None:
self.timer = core.timer(0, self.event.set) self.timer = get_hub().reactor.timer(0, self.event.set)
# using core.timer(0, ...) to let other active events call update() before Event.wait() returns # using timer(0, ...) to let other active events call update() before Event.wait() returns
def select(rlist, wlist, xlist, timeout=None): def select(rlist, wlist, xlist, timeout=None):
...@@ -54,10 +55,15 @@ def select(rlist, wlist, xlist, timeout=None): ...@@ -54,10 +55,15 @@ def select(rlist, wlist, xlist, timeout=None):
result = SelectResult() result = SelectResult()
try: try:
try: try:
reactor = get_hub().reactor
for readfd in rlist: for readfd in rlist:
allevents.append(core.read_event(get_fileno(readfd), result.update, arg=readfd)) event = reactor.read_event(get_fileno(readfd))
event.add(None, result.update, arg=readfd)
allevents.append(event)
for writefd in wlist: for writefd in wlist:
allevents.append(core.write_event(get_fileno(writefd), result.update, arg=writefd)) event = reactor.write_event(get_fileno(writefd))
event.add(None, result.update, arg=writefd)
allevents.append(event)
except IOError, ex: except IOError, ex:
raise error(*ex.args) raise error(*ex.args)
result.event.wait(timeout=timeout) result.event.wait(timeout=timeout)
......
...@@ -4,8 +4,8 @@ import sys ...@@ -4,8 +4,8 @@ import sys
import errno import errno
import traceback import traceback
from gevent import socket from gevent import socket
from gevent import core
from gevent.baseserver import BaseServer from gevent.baseserver import BaseServer
from gevent.hub import get_hub
__all__ = ['StreamServer'] __all__ = ['StreamServer']
...@@ -88,7 +88,8 @@ class StreamServer(BaseServer): ...@@ -88,7 +88,8 @@ class StreamServer(BaseServer):
def start_accepting(self): def start_accepting(self):
if self._accept_event is None: if self._accept_event is None:
self._accept_event = core.read_event(self.socket.fileno(), self._do_accept, persist=True) self._accept_event = get_hub().reactor.read_event(self.socket.fileno(), persist=True)
self._accept_event.add(None, self._do_accept)
def _start_accepting_if_started(self, _event=None): def _start_accepting_if_started(self, _event=None):
if self.started: if self.started:
...@@ -139,7 +140,7 @@ class StreamServer(BaseServer): ...@@ -139,7 +140,7 @@ class StreamServer(BaseServer):
traceback.print_exc() traceback.print_exc()
if self.delay >= 0: if self.delay >= 0:
self.stop_accepting() self.stop_accepting()
self._start_accepting_timer = core.timer(self.delay, self.start_accepting) self._start_accepting_timer = get_hub().reactor.timer(self.delay, self._start_accepting_if_started)
self.delay = min(self.max_delay, self.delay * 2) self.delay = min(self.max_delay, self.delay * 2)
def is_fatal_error(self, ex): def is_fatal_error(self, ex):
......
...@@ -75,7 +75,6 @@ __imports__ = ['error', ...@@ -75,7 +75,6 @@ __imports__ = ['error',
import sys import sys
import time import time
import random
import re import re
is_windows = sys.platform == 'win32' is_windows = sys.platform == 'win32'
...@@ -123,18 +122,9 @@ for name in __socket__.__all__: ...@@ -123,18 +122,9 @@ for name in __socket__.__all__:
del name, value del name, value
if 'inet_ntop' not in globals():
# inet_ntop is required by our implementation of getaddrinfo
def inet_ntop(address_family, packed_ip):
if address_family == AF_INET:
return inet_ntoa(packed_ip)
# XXX: ipv6 won't work on windows
raise NotImplementedError('inet_ntop() is not available on this platform')
# XXX: implement blocking functions that are not yet implemented # XXX: implement blocking functions that are not yet implemented
from gevent.hub import getcurrent, get_hub from gevent.hub import getcurrent, get_hub, Waiter
from gevent import core from gevent import core
_ip4_re = re.compile('^[\d\.]+$') _ip4_re = re.compile('^[\d\.]+$')
...@@ -148,29 +138,37 @@ def _wait_helper(ev, evtype): ...@@ -148,29 +138,37 @@ def _wait_helper(ev, evtype):
current.switch(ev) current.switch(ev)
def wait_read(fileno, timeout=None, timeout_exc=timeout('timed out'), event=None): def wait(event, timeout=None, timeout_exc=timeout('timed out')):
"""Block the current greenlet until *fileno* is ready to read. """Block the current greenlet until *event* is ready.
If *timeout* is non-negative, then *timeout_exc* is raised after *timeout* second has passed. If *timeout* is non-negative, then *timeout_exc* is raised after *timeout* second has passed.
By default *timeout_exc* is ``socket.timeout('timed out')``. By default *timeout_exc* is ``socket.timeout('timed out')``.
If :func:`cancel_wait` is called, raise ``socket.error(EBADF, 'File descriptor was closed in another greenlet')``. If :func:`cancel_wait` is called, raise ``socket.error(EBADF, 'File descriptor was closed in another greenlet')``.
""" """
if event is None:
event = core.read_event(fileno, _wait_helper, timeout, (getcurrent(), timeout_exc))
else:
assert event.callback == _wait_helper, event.callback
assert event.arg is None, 'This event is already used by another greenlet: %r' % (event.arg, ) assert event.arg is None, 'This event is already used by another greenlet: %r' % (event.arg, )
event.arg = (getcurrent(), timeout_exc) event.add(timeout, _wait_helper, (getcurrent(), timeout_exc))
event.add(timeout)
try: try:
switch_result = get_hub().switch() switch_result = get_hub().switch()
assert event is switch_result, 'Invalid switch into wait_read(): %r' % (switch_result, ) assert event is switch_result, 'Invalid switch into wait(%r): %r' % (event, switch_result, )
finally: finally:
event.cancel() event.cancel()
event.arg = None event.arg = None
def wait_read(fileno, timeout=None, timeout_exc=timeout('timed out'), event=None):
"""Block the current greenlet until *fileno* is ready to read.
If *timeout* is non-negative, then *timeout_exc* is raised after *timeout* second has passed.
By default *timeout_exc* is ``socket.timeout('timed out')``.
If :func:`cancel_wait` is called, raise ``socket.error(EBADF, 'File descriptor was closed in another greenlet')``.
"""
if event is None:
event = get_hub().reactor.read_event(fileno)
return wait(event, timeout, timeout_exc)
def wait_write(fileno, timeout=None, timeout_exc=timeout('timed out'), event=None): def wait_write(fileno, timeout=None, timeout_exc=timeout('timed out'), event=None):
"""Block the current greenlet until *fileno* is ready to write. """Block the current greenlet until *fileno* is ready to write.
...@@ -180,18 +178,8 @@ def wait_write(fileno, timeout=None, timeout_exc=timeout('timed out'), event=Non ...@@ -180,18 +178,8 @@ def wait_write(fileno, timeout=None, timeout_exc=timeout('timed out'), event=Non
If :func:`cancel_wait` is called, raise ``socket.error(EBADF, 'File descriptor was closed in another greenlet')``. If :func:`cancel_wait` is called, raise ``socket.error(EBADF, 'File descriptor was closed in another greenlet')``.
""" """
if event is None: if event is None:
event = core.write_event(fileno, _wait_helper, timeout, (getcurrent(), timeout_exc)) event = get_hub().reactor.write_event(fileno)
else: return wait(event, timeout, timeout_exc)
assert event.callback == _wait_helper, event.callback
assert event.arg is None, 'This event is already used by another greenlet: %r' % (event.arg, )
event.arg = (getcurrent(), timeout_exc)
event.add(timeout)
try:
switch_result = get_hub().switch()
assert event is switch_result, 'Invalid switch into wait_write(): %r' % (switch_result, )
finally:
event.arg = None
event.cancel()
def wait_readwrite(fileno, timeout=None, timeout_exc=timeout('timed out'), event=None): def wait_readwrite(fileno, timeout=None, timeout_exc=timeout('timed out'), event=None):
...@@ -203,18 +191,8 @@ def wait_readwrite(fileno, timeout=None, timeout_exc=timeout('timed out'), event ...@@ -203,18 +191,8 @@ def wait_readwrite(fileno, timeout=None, timeout_exc=timeout('timed out'), event
If :func:`cancel_wait` is called, raise ``socket.error(EBADF, 'File descriptor was closed in another greenlet')``. If :func:`cancel_wait` is called, raise ``socket.error(EBADF, 'File descriptor was closed in another greenlet')``.
""" """
if event is None: if event is None:
event = core.readwrite_event(fileno, _wait_helper, timeout, (getcurrent(), timeout_exc)) event = get_hub().reactor.readwrite_event(fileno)
else: return wait(event, timeout, timeout_exc)
assert event.callback == _wait_helper, event.callback
assert event.arg is None, 'This event is already used by another greenlet: %r' % (event.arg, )
event.arg = (getcurrent(), timeout_exc)
event.add(timeout)
try:
switch_result = get_hub().switch()
assert event is switch_result, 'Invalid switch into wait_readwrite(): %r' % (switch_result, )
finally:
event.arg = None
event.cancel()
def __cancel_wait(event): def __cancel_wait(event):
...@@ -225,7 +203,7 @@ def __cancel_wait(event): ...@@ -225,7 +203,7 @@ def __cancel_wait(event):
def cancel_wait(event): def cancel_wait(event):
core.active_event(__cancel_wait, event) get_hub().reactor.active_event(__cancel_wait, event)
if sys.version_info[:2] <= (2, 4): if sys.version_info[:2] <= (2, 4):
...@@ -290,9 +268,10 @@ class socket(object): ...@@ -290,9 +268,10 @@ class socket(object):
self._sock = _sock self._sock = _sock
self.timeout = _socket.getdefaulttimeout() self.timeout = _socket.getdefaulttimeout()
self._sock.setblocking(0) self._sock.setblocking(0)
self._read_event = core.event(core.EV_READ, self.fileno(), _wait_helper) reactor = get_hub().reactor
self._write_event = core.event(core.EV_WRITE, self.fileno(), _wait_helper) self._read_event = reactor.read_event(self.fileno())
self._rw_event = core.event(core.EV_READ | core.EV_WRITE, self.fileno(), _wait_helper) self._write_event = reactor.write_event(self.fileno())
self._rw_event = reactor.readwrite_event(self.fileno())
def __repr__(self): def __repr__(self):
return '<%s at %s %s>' % (type(self).__name__, hex(id(self)), self._formatinfo()) return '<%s at %s %s>' % (type(self).__name__, hex(id(self)), self._formatinfo())
...@@ -334,7 +313,7 @@ class socket(object): ...@@ -334,7 +313,7 @@ class socket(object):
if ex[0] != EWOULDBLOCK or self.timeout == 0.0: if ex[0] != EWOULDBLOCK or self.timeout == 0.0:
raise raise
sys.exc_clear() sys.exc_clear()
wait_read(sock.fileno(), timeout=self.timeout, event=self._read_event) wait(self._read_event, timeout=self.timeout)
return socket(_sock=client_socket), address return socket(_sock=client_socket), address
def close(self): def close(self):
...@@ -361,7 +340,7 @@ class socket(object): ...@@ -361,7 +340,7 @@ class socket(object):
if not result or result == EISCONN: if not result or result == EISCONN:
break break
elif (result in (EWOULDBLOCK, EINPROGRESS, EALREADY)) or (result == EINVAL and is_windows): elif (result in (EWOULDBLOCK, EINPROGRESS, EALREADY)) or (result == EINVAL and is_windows):
wait_readwrite(sock.fileno(), event=self._rw_event) wait(self._rw_event)
else: else:
raise error(result, strerror(result)) raise error(result, strerror(result))
else: else:
...@@ -377,7 +356,7 @@ class socket(object): ...@@ -377,7 +356,7 @@ class socket(object):
timeleft = end - time.time() timeleft = end - time.time()
if timeleft <= 0: if timeleft <= 0:
raise timeout('timed out') raise timeout('timed out')
wait_readwrite(sock.fileno(), timeout=timeleft, event=self._rw_event) wait(self._rw_event, timeout=timeleft)
else: else:
raise error(result, strerror(result)) raise error(result, strerror(result))
...@@ -417,7 +396,7 @@ class socket(object): ...@@ -417,7 +396,7 @@ class socket(object):
# QQQ without clearing exc_info test__refcount.test_clean_exit fails # QQQ without clearing exc_info test__refcount.test_clean_exit fails
sys.exc_clear() sys.exc_clear()
try: try:
wait_read(sock.fileno(), timeout=self.timeout, event=self._read_event) wait(self._read_event, timeout=self.timeout)
except error, ex: except error, ex:
if ex[0] == EBADF: if ex[0] == EBADF:
return '' return ''
...@@ -432,7 +411,7 @@ class socket(object): ...@@ -432,7 +411,7 @@ class socket(object):
if ex[0] != EWOULDBLOCK or self.timeout == 0.0: if ex[0] != EWOULDBLOCK or self.timeout == 0.0:
raise raise
sys.exc_clear() sys.exc_clear()
wait_read(sock.fileno(), timeout=self.timeout, event=self._read_event) wait(self._read_event, timeout=self.timeout)
def recvfrom_into(self, *args): def recvfrom_into(self, *args):
sock = self._sock sock = self._sock
...@@ -443,7 +422,7 @@ class socket(object): ...@@ -443,7 +422,7 @@ class socket(object):
if ex[0] != EWOULDBLOCK or self.timeout == 0.0: if ex[0] != EWOULDBLOCK or self.timeout == 0.0:
raise raise
sys.exc_clear() sys.exc_clear()
wait_read(sock.fileno(), timeout=self.timeout, event=self._read_event) wait(self._read_event, timeout=self.timeout)
def recv_into(self, *args): def recv_into(self, *args):
sock = self._sock sock = self._sock
...@@ -457,7 +436,7 @@ class socket(object): ...@@ -457,7 +436,7 @@ class socket(object):
raise raise
sys.exc_clear() sys.exc_clear()
try: try:
wait_read(sock.fileno(), timeout=self.timeout, event=self._read_event) wait(self._read_event, timeout=self.timeout)
except error, ex: except error, ex:
if ex[0] == EBADF: if ex[0] == EBADF:
return 0 return 0
...@@ -474,7 +453,7 @@ class socket(object): ...@@ -474,7 +453,7 @@ class socket(object):
raise raise
sys.exc_clear() sys.exc_clear()
try: try:
wait_write(sock.fileno(), timeout=timeout, event=self._write_event) wait(self._write_event, timeout=timeout)
except error, ex: except error, ex:
if ex[0] == EBADF: if ex[0] == EBADF:
return 0 return 0
...@@ -515,7 +494,7 @@ class socket(object): ...@@ -515,7 +494,7 @@ class socket(object):
if ex[0] != EWOULDBLOCK or timeout == 0.0: if ex[0] != EWOULDBLOCK or timeout == 0.0:
raise raise
sys.exc_clear() sys.exc_clear()
wait_write(sock.fileno(), timeout=self.timeout, event=self._write_event) wait(self._write_event, timeout=self.timeout)
try: try:
return sock.sendto(*args) return sock.sendto(*args)
except error, ex2: except error, ex2:
...@@ -640,100 +619,39 @@ def create_connection(address, timeout=_GLOBAL_DEFAULT_TIMEOUT, source_address=N ...@@ -640,100 +619,39 @@ def create_connection(address, timeout=_GLOBAL_DEFAULT_TIMEOUT, source_address=N
raise msg raise msg
try: def gethostbyname(host):
from gevent.dns import resolve_ipv4, resolve_ipv6 # artificial limitations that regular gethostbyname has
except Exception: if host is None:
import traceback raise TypeError('gethostbyname() argument must be string')
traceback.print_exc() host = host.encode('ascii')
__implements__.remove('gethostbyname') res = getaddrinfo(host, 0)
__implements__.remove('getaddrinfo') if res:
else: return res[0][4][0]
def gethostbyname(hostname):
""":func:`socket.gethostbyname` implemented using :mod:`gevent.dns`.
Differs in the following ways: def getaddrinfo(host, port, family=0, socktype=0, proto=0, flags=0):
dns = get_hub().reactor.dns
* raises :class:`DNSError` (a subclass of :class:`socket.gaierror`) with dns error if dns is None:
codes instead of standard socket error codes return _socket.getaddrinfo(host, port, family, socktype, proto, flags)
* does not support ``/etc/hosts`` but calls the original :func:`socket.gethostbyname`
if *hostname* has no dots
* does not iterate through all addresses, instead picks a random one each time
"""
# TODO: this is supposed to iterate through all the addresses
# could use a global dict(hostname, iter)
# - fix these nasty hacks for localhost, ips, etc.
if not isinstance(hostname, str) or '.' not in hostname:
return _socket.gethostbyname(hostname)
if _ip4_re.match(hostname):
return hostname
if hostname == _socket.gethostname():
return _socket.gethostbyname(hostname)
_ttl, addrs = resolve_ipv4(hostname)
return inet_ntoa(random.choice(addrs))
def getaddrinfo(host, port, *args, **kwargs):
"""*Some* approximation of :func:`socket.getaddrinfo` implemented using :mod:`gevent.dns`.
If *host* is not a string, does not has any dots or is a numeric IP address, then
the standard :func:`socket.getaddrinfo` is called.
Otherwise, calls either :func:`resolve_ipv4` or :func:`resolve_ipv6` and
formats the result the way :func:`socket.getaddrinfo` does it.
Differs in the following ways:
* raises :class:`DNSError` (a subclass of :class:`gaierror`) with libevent-dns error
codes instead of standard socket error codes
* IPv6 support is untested.
* AF_UNSPEC only tries IPv4
* only supports TCP, UDP, IP protocols
* port must be numeric, does not support string service names. see socket.getservbyname
* *flags* argument is ignored
Additionally, supports *evdns_flags* keyword arguments (default ``0``) that is passed
to :mod:`dns` functions.
"""
family, socktype, proto, _flags = args + (None, ) * (4 - len(args))
if isinstance(host, unicode):
host = host.encode('idna')
if not isinstance(host, str) or '.' not in host or _ip4_re.match(host):
return _socket.getaddrinfo(host, port, *args)
evdns_flags = kwargs.pop('evdns_flags', 0)
if kwargs:
raise TypeError('Unsupported keyword arguments: %s' % (kwargs.keys(), ))
if family in (None, AF_INET, AF_UNSPEC):
family = AF_INET
# TODO: AF_UNSPEC means try both AF_INET and AF_INET6
_ttl, addrs = resolve_ipv4(host, evdns_flags)
elif family == AF_INET6:
_ttl, addrs = resolve_ipv6(host, evdns_flags)
else: else:
raise NotImplementedError('family is not among AF_UNSPEC/AF_INET/AF_INET6: %r' % (family, )) waiter = Waiter()
request = dns.getaddrinfo(waiter.switch_args, host, port, family, socktype, proto, flags)
socktype_proto = [(SOCK_STREAM, 6), (SOCK_DGRAM, 17), (SOCK_RAW, 0)] try:
if socktype: result, request_error, _arg = waiter.get()
socktype_proto = [(x, y) for (x, y) in socktype_proto if socktype == x] except:
if proto: if request is not None:
socktype_proto = [(x, y) for (x, y) in socktype_proto if proto == y] request.cancel()
raise
result = [] if request_error is None:
for addr in addrs:
for socktype, proto in socktype_proto:
result.append((family, socktype, proto, '', (inet_ntop(family, addr), port)))
return result return result
# TODO libevent2 has getaddrinfo that is probably better than the hack above; should wrap that. raise request_error
_have_ssl = False
try: try:
from gevent.ssl import sslwrap_simple as ssl, SSLError as sslerror, SSLSocket as SSLType from gevent.ssl import sslwrap_simple as ssl, SSLError as sslerror, SSLSocket as SSLType
_have_ssl = True _have_ssl = True
except ImportError: except ImportError:
pass _have_ssl = False
if sys.version_info[:2] <= (2, 5) and _have_ssl: if sys.version_info[:2] <= (2, 5) and _have_ssl:
......
...@@ -13,8 +13,7 @@ to arbitrary code. ...@@ -13,8 +13,7 @@ to arbitrary code.
which no switches occur, :class:`Timeout` is powerless. which no switches occur, :class:`Timeout` is powerless.
""" """
from gevent import core from gevent.hub import getcurrent, _NONE, get_hub
from gevent.hub import getcurrent, _NONE
__all__ = ['Timeout', __all__ = ['Timeout',
'with_timeout'] 'with_timeout']
...@@ -86,17 +85,17 @@ class Timeout(BaseException): ...@@ -86,17 +85,17 @@ class Timeout(BaseException):
def __init__(self, seconds=None, exception=None): def __init__(self, seconds=None, exception=None):
self.seconds = seconds self.seconds = seconds
self.exception = exception self.exception = exception
self.timer = None self.timer = get_hub().reactor.timer()
def start(self): def start(self):
"""Schedule the timeout.""" """Schedule the timeout."""
assert not self.pending, '%r is already started; to restart it, cancel it first' % self assert not self.pending, '%r is already started; to restart it, cancel it first' % self
if self.seconds is None: # "fake" timeout (never expires) if self.seconds is None: # "fake" timeout (never expires)
self.timer = None pass
elif self.exception is None or self.exception is False: # timeout that raises self elif self.exception is None or self.exception is False: # timeout that raises self
self.timer = core.timer(self.seconds, getcurrent().throw, self) self.timer.add(self.seconds, getcurrent().throw, self)
else: # regular timeout with user-provided exception else: # regular timeout with user-provided exception
self.timer = core.timer(self.seconds, getcurrent().throw, self.exception) self.timer.add(self.seconds, getcurrent().throw, self.exception)
@classmethod @classmethod
def start_new(cls, timeout=None, exception=None): def start_new(cls, timeout=None, exception=None):
...@@ -167,7 +166,7 @@ class Timeout(BaseException): ...@@ -167,7 +166,7 @@ class Timeout(BaseException):
return '%s second%s (%s)' % (self.seconds, suffix, self.exception) return '%s second%s (%s)' % (self.seconds, suffix, self.exception)
def __enter__(self): def __enter__(self):
if self.timer is None: if not self.timer.pending:
self.start() self.start()
return self return self
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment