# Copyright (c) 2009-2012 Denis Bilenko. See LICENSE for details. # This directive, supported in Cython 0.24+, causes sources files to be # much smaller and thus cythonpp.py to be slightly faster. But it does make # debugging more difficult. # cython: emit_code_comments=False cimport cython cimport libev # Note this is not the standard cython 'cpython' (which has a backwards compat alias of 'python') # it's our custom def. If it's not on the include path, we get warned. from python cimport * # Work around lack of absolute_import in Cython # Note for PY3: not doing so will leave reference to locals() on import # (reproducible under Python 3.3, not under Python 3.4; see test__refcount_core.py) sys = __import__('sys', level=0) os = __import__('os', level=0) traceback = __import__('traceback', level=0) signalmodule = __import__('signal', level=0) __all__ = ['get_version', 'get_header_version', 'supported_backends', 'recommended_backends', 'embeddable_backends', 'time', 'loop'] cdef tuple integer_types if sys.version_info[0] >= 3: integer_types = int, else: integer_types = (int, long) cdef extern from "callbacks.h": void gevent_callback_io(libev.ev_loop, void*, int) void gevent_callback_timer(libev.ev_loop, void*, int) void gevent_callback_signal(libev.ev_loop, void*, int) void gevent_callback_idle(libev.ev_loop, void*, int) void gevent_callback_prepare(libev.ev_loop, void*, int) void gevent_callback_check(libev.ev_loop, void*, int) void gevent_callback_fork(libev.ev_loop, void*, int) void gevent_callback_async(libev.ev_loop, void*, int) void gevent_callback_child(libev.ev_loop, void*, int) void gevent_callback_stat(libev.ev_loop, void*, int) void gevent_run_callbacks(libev.ev_loop, void*, int) void gevent_periodic_signal_check(libev.ev_loop, void*, int) void gevent_call(loop, callback) void gevent_noop(libev.ev_loop, void*, int) cdef extern from *: int errno cdef extern from "stathelper.c": object _pystat_fromstructstat(void*) UNDEF = libev.EV_UNDEF NONE = libev.EV_NONE READ = libev.EV_READ WRITE = libev.EV_WRITE TIMER = libev.EV_TIMER PERIODIC = libev.EV_PERIODIC SIGNAL = libev.EV_SIGNAL CHILD = libev.EV_CHILD STAT = libev.EV_STAT IDLE = libev.EV_IDLE PREPARE = libev.EV_PREPARE CHECK = libev.EV_CHECK EMBED = libev.EV_EMBED FORK = libev.EV_FORK CLEANUP = libev.EV_CLEANUP ASYNC = libev.EV_ASYNC CUSTOM = libev.EV_CUSTOM ERROR = libev.EV_ERROR READWRITE = libev.EV_READ | libev.EV_WRITE MINPRI = libev.EV_MINPRI MAXPRI = libev.EV_MAXPRI BACKEND_PORT = libev.EVBACKEND_PORT BACKEND_KQUEUE = libev.EVBACKEND_KQUEUE BACKEND_EPOLL = libev.EVBACKEND_EPOLL BACKEND_POLL = libev.EVBACKEND_POLL BACKEND_SELECT = libev.EVBACKEND_SELECT FORKCHECK = libev.EVFLAG_FORKCHECK NOINOTIFY = libev.EVFLAG_NOINOTIFY SIGNALFD = libev.EVFLAG_SIGNALFD NOSIGMASK = libev.EVFLAG_NOSIGMASK @cython.internal cdef class _EVENTSType: def __repr__(self): return 'gevent.core.EVENTS' cdef public object GEVENT_CORE_EVENTS = _EVENTSType() EVENTS = GEVENT_CORE_EVENTS def get_version(): return 'libev-%d.%02d' % (libev.ev_version_major(), libev.ev_version_minor()) def get_header_version(): return 'libev-%d.%02d' % (libev.EV_VERSION_MAJOR, libev.EV_VERSION_MINOR) # This list backends in the order they are actually tried by libev _flags = [(libev.EVBACKEND_PORT, 'port'), (libev.EVBACKEND_KQUEUE, 'kqueue'), (libev.EVBACKEND_EPOLL, 'epoll'), (libev.EVBACKEND_POLL, 'poll'), (libev.EVBACKEND_SELECT, 'select'), (libev.EVFLAG_NOENV, 'noenv'), (libev.EVFLAG_FORKCHECK, 'forkcheck'), (libev.EVFLAG_NOINOTIFY, 'noinotify'), (libev.EVFLAG_SIGNALFD, 'signalfd'), (libev.EVFLAG_NOSIGMASK, 'nosigmask')] _flags_str2int = dict((string, flag) for (flag, string) in _flags) _events = [(libev.EV_READ, 'READ'), (libev.EV_WRITE, 'WRITE'), (libev.EV__IOFDSET, '_IOFDSET'), (libev.EV_PERIODIC, 'PERIODIC'), (libev.EV_SIGNAL, 'SIGNAL'), (libev.EV_CHILD, 'CHILD'), (libev.EV_STAT, 'STAT'), (libev.EV_IDLE, 'IDLE'), (libev.EV_PREPARE, 'PREPARE'), (libev.EV_CHECK, 'CHECK'), (libev.EV_EMBED, 'EMBED'), (libev.EV_FORK, 'FORK'), (libev.EV_CLEANUP, 'CLEANUP'), (libev.EV_ASYNC, 'ASYNC'), (libev.EV_CUSTOM, 'CUSTOM'), (libev.EV_ERROR, 'ERROR')] cpdef _flags_to_list(unsigned int flags): cdef list result = [] for code, value in _flags: if flags & code: result.append(value) flags &= ~code if not flags: break if flags: result.append(flags) return result if sys.version_info[0] >= 3: basestring = (bytes, str) else: basestring = __builtins__.basestring cpdef unsigned int _flags_to_int(object flags) except? -1: # Note, that order does not matter, libev has its own predefined order if not flags: return 0 if isinstance(flags, integer_types): return flags cdef unsigned int result = 0 try: if isinstance(flags, basestring): flags = flags.split(',') for value in flags: value = value.strip().lower() if value: result |= _flags_str2int[value] except KeyError as ex: raise ValueError('Invalid backend or flag: %s\nPossible values: %s' % (ex, ', '.join(sorted(_flags_str2int.keys())))) return result cdef str _str_hex(object flag): if isinstance(flag, integer_types): return hex(flag) return str(flag) cpdef _check_flags(unsigned int flags): cdef list as_list flags &= libev.EVBACKEND_MASK if not flags: return if not (flags & libev.EVBACKEND_ALL): raise ValueError('Invalid value for backend: 0x%x' % flags) if not (flags & libev.ev_supported_backends()): as_list = [_str_hex(x) for x in _flags_to_list(flags)] raise ValueError('Unsupported backend: %s' % '|'.join(as_list)) cpdef _events_to_str(int events): cdef list result = [] cdef int c_flag for (flag, string) in _events: c_flag = flag if events & c_flag: result.append(string) events = events & (~c_flag) if not events: break if events: result.append(hex(events)) return '|'.join(result) def supported_backends(): return _flags_to_list(libev.ev_supported_backends()) def recommended_backends(): return _flags_to_list(libev.ev_recommended_backends()) def embeddable_backends(): return _flags_to_list(libev.ev_embeddable_backends()) def time(): return libev.ev_time() #define LOOP_PROPERTY(NAME) property NAME: \ \ def __get__(self): \ CHECK_LOOP3(self) \ return self._ptr.NAME cdef bint _default_loop_destroyed = False #define CHECK_LOOP2(LOOP) \ if not LOOP._ptr: \ raise ValueError('operation on destroyed loop') #define CHECK_LOOP3(LOOP) \ if not LOOP._ptr: \ raise ValueError('operation on destroyed loop') cdef public class loop [object PyGeventLoopObject, type PyGeventLoop_Type]: cdef libev.ev_loop* _ptr cdef public object error_handler cdef libev.ev_prepare _prepare cdef public list _callbacks cdef libev.ev_timer _timer0 #ifdef _WIN32 cdef libev.ev_timer _periodic_signal_checker #endif def __init__(self, object flags=None, object default=None, size_t ptr=0): cdef unsigned int c_flags cdef object old_handler = None libev.ev_prepare_init(&self._prepare, gevent_run_callbacks) #ifdef _WIN32 libev.ev_timer_init(&self._periodic_signal_checker, gevent_periodic_signal_check, 0.3, 0.3) #endif libev.ev_timer_init(&self._timer0, gevent_noop, 0.0, 0.0) if ptr: self._ptr = ptr else: c_flags = _flags_to_int(flags) _check_flags(c_flags) c_flags |= libev.EVFLAG_NOENV c_flags |= libev.EVFLAG_FORKCHECK if default is None: default = True if _default_loop_destroyed: default = False if default: self._ptr = libev.gevent_ev_default_loop(c_flags) if not self._ptr: raise SystemError("ev_default_loop(%s) failed" % (c_flags, )) #ifdef _WIN32 libev.ev_timer_start(self._ptr, &self._periodic_signal_checker) libev.ev_unref(self._ptr) #endif else: self._ptr = libev.ev_loop_new(c_flags) if not self._ptr: raise SystemError("ev_loop_new(%s) failed" % (c_flags, )) if default or __SYSERR_CALLBACK is None: set_syserr_cb(self._handle_syserr) libev.ev_prepare_start(self._ptr, &self._prepare) libev.ev_unref(self._ptr) self._callbacks = [] cdef _run_callbacks(self): cdef callback cb cdef object callbacks cdef int count = 1000 libev.ev_timer_stop(self._ptr, &self._timer0) while self._callbacks and count > 0: callbacks = self._callbacks self._callbacks = [] for cb in callbacks: libev.ev_unref(self._ptr) gevent_call(self, cb) count -= 1 if self._callbacks: libev.ev_timer_start(self._ptr, &self._timer0) def _stop_watchers(self): if libev.ev_is_active(&self._prepare): libev.ev_ref(self._ptr) libev.ev_prepare_stop(self._ptr, &self._prepare) #ifdef _WIN32 if libev.ev_is_active(&self._periodic_signal_checker): libev.ev_ref(self._ptr) libev.ev_timer_stop(self._ptr, &self._periodic_signal_checker) #endif def destroy(self): global _default_loop_destroyed if self._ptr: self._stop_watchers() if __SYSERR_CALLBACK == self._handle_syserr: set_syserr_cb(None) if libev.ev_is_default_loop(self._ptr): _default_loop_destroyed = True libev.ev_loop_destroy(self._ptr) self._ptr = NULL def __dealloc__(self): if self._ptr: self._stop_watchers() if not libev.ev_is_default_loop(self._ptr): libev.ev_loop_destroy(self._ptr) self._ptr = NULL property ptr: def __get__(self): return self._ptr property WatcherType: def __get__(self): return watcher property MAXPRI: def __get__(self): return libev.EV_MAXPRI property MINPRI: def __get__(self): return libev.EV_MINPRI def _handle_syserr(self, message, errno): if sys.version_info[0] >= 3: message = message.decode() self.handle_error(None, SystemError, SystemError(message + ': ' + os.strerror(errno)), None) cpdef handle_error(self, context, type, value, tb): cdef object handle_error cdef object error_handler = self.error_handler if error_handler is not None: # we do want to do getattr every time so that setting Hub.handle_error property just works handle_error = getattr(error_handler, 'handle_error', error_handler) handle_error(context, type, value, tb) else: self._default_handle_error(context, type, value, tb) cpdef _default_handle_error(self, context, type, value, tb): # note: Hub sets its own error handler so this is not used by gevent # this is here to make core.loop usable without the rest of gevent traceback.print_exception(type, value, tb) if self._ptr: libev.ev_break(self._ptr, libev.EVBREAK_ONE) def run(self, nowait=False, once=False): CHECK_LOOP2(self) cdef unsigned int flags = 0 if nowait: flags |= libev.EVRUN_NOWAIT if once: flags |= libev.EVRUN_ONCE with nogil: libev.ev_run(self._ptr, flags) def reinit(self): if self._ptr: libev.ev_loop_fork(self._ptr) def ref(self): CHECK_LOOP2(self) libev.ev_ref(self._ptr) def unref(self): CHECK_LOOP2(self) libev.ev_unref(self._ptr) def break_(self, int how=libev.EVBREAK_ONE): CHECK_LOOP2(self) libev.ev_break(self._ptr, how) def verify(self): CHECK_LOOP2(self) libev.ev_verify(self._ptr) def now(self): CHECK_LOOP2(self) return libev.ev_now(self._ptr) def update(self): CHECK_LOOP2(self) libev.ev_now_update(self._ptr) def __repr__(self): return '<%s at 0x%x %s>' % (self.__class__.__name__, id(self), self._format()) property default: def __get__(self): CHECK_LOOP3(self) return True if libev.ev_is_default_loop(self._ptr) else False property iteration: def __get__(self): CHECK_LOOP3(self) return libev.ev_iteration(self._ptr) property depth: def __get__(self): CHECK_LOOP3(self) return libev.ev_depth(self._ptr) property backend_int: def __get__(self): CHECK_LOOP3(self) return libev.ev_backend(self._ptr) property backend: def __get__(self): CHECK_LOOP3(self) cdef unsigned int backend = libev.ev_backend(self._ptr) for key, value in _flags: if key == backend: return value return backend property pendingcnt: def __get__(self): CHECK_LOOP3(self) return libev.ev_pending_count(self._ptr) #ifdef _WIN32 def io(self, libev.vfd_socket_t fd, int events, ref=True, priority=None): return io(self, fd, events, ref, priority) #else def io(self, int fd, int events, ref=True, priority=None): return io(self, fd, events, ref, priority) #endif def timer(self, double after, double repeat=0.0, ref=True, priority=None): return timer(self, after, repeat, ref, priority) def signal(self, int signum, ref=True, priority=None): return signal(self, signum, ref, priority) def idle(self, ref=True, priority=None): return idle(self, ref, priority) def prepare(self, ref=True, priority=None): return prepare(self, ref, priority) def check(self, ref=True, priority=None): return check(self, ref, priority) def fork(self, ref=True, priority=None): return fork(self, ref, priority) def async(self, ref=True, priority=None): return async(self, ref, priority) #ifdef _WIN32 #else def child(self, int pid, bint trace=0, ref=True): return child(self, pid, trace, ref) def install_sigchld(self): libev.gevent_install_sigchld_handler() def reset_sigchld(self): libev.gevent_reset_sigchld_handler() #endif def stat(self, str path, float interval=0.0, ref=True, priority=None): return stat(self, path, interval, ref, priority) def run_callback(self, func, *args): CHECK_LOOP2(self) cdef callback cb = callback(func, args) self._callbacks.append(cb) libev.ev_ref(self._ptr) return cb def _format(self): if not self._ptr: return 'destroyed' cdef object msg = self.backend if self.default: msg += ' default' msg += ' pending=%s' % self.pendingcnt #ifdef LIBEV_EMBED msg += self._format_details() #endif return msg #ifdef LIBEV_EMBED def _format_details(self): cdef str msg = '' cdef object fileno = self.fileno() cdef object sigfd = None cdef object activecnt = None try: sigfd = self.sigfd except AttributeError: sigfd = None try: activecnt = self.activecnt except AttributeError: pass if activecnt is not None: msg += ' ref=' + repr(activecnt) if fileno is not None: msg += ' fileno=' + repr(fileno) if sigfd is not None and sigfd != -1: msg += ' sigfd=' + repr(sigfd) return msg def fileno(self): cdef int fd if self._ptr: fd = self._ptr.backend_fd if fd >= 0: return fd LOOP_PROPERTY(activecnt) LOOP_PROPERTY(sig_pending) #if EV_USE_SIGNALFD LOOP_PROPERTY(sigfd) #endif property origflags: def __get__(self): CHECK_LOOP3(self) return _flags_to_list(self._ptr.origflags) property origflags_int: def __get__(self): CHECK_LOOP3(self) return self._ptr.origflags #endif cdef public class callback [object PyGeventCallbackObject, type PyGeventCallback_Type]: cdef public object callback cdef public tuple args def __init__(self, callback, args): self.callback = callback self.args = args def stop(self): self.callback = None self.args = None # Note, that __nonzero__ and pending are different # nonzero is used in contexts where we need to know whether to schedule another callback, # so it's true if it's pending or currently running # 'pending' has the same meaning as libev watchers: it is cleared before entering callback def __nonzero__(self): # it's nonzero if it's pending or currently executing return self.args is not None property pending: def __get__(self): return self.callback is not None def __repr__(self): if Py_ReprEnter(self) != 0: return "<...>" try: format = self._format() result = "<%s at 0x%x%s" % (self.__class__.__name__, id(self), format) if self.pending: result += " pending" if self.callback is not None: result += " callback=%r" % (self.callback, ) if self.args is not None: result += " args=%r" % (self.args, ) if self.callback is None and self.args is None: result += " stopped" return result + ">" finally: Py_ReprLeave(self) def _format(self): return '' #define PYTHON_INCREF if not self._flags & 1: \ Py_INCREF(self) \ self._flags |= 1 #define LIBEV_UNREF if self._flags & 6 == 4: \ libev.ev_unref(self.loop._ptr) \ self._flags |= 2 # about readonly _flags attribute: # bit #1 set if object owns Python reference to itself (Py_INCREF was called and we must call Py_DECREF later) # bit #2 set if ev_unref() was called and we must call ev_ref() later # bit #3 set if user wants to call ev_unref() before start() #define WATCHER_BASE(TYPE) \ cdef public loop loop \ cdef object _callback \ cdef public tuple args \ cdef readonly int _flags \ cdef libev.ev_##TYPE _watcher \ \ property ref: \ \ def __get__(self): \ return False if self._flags & 4 else True \ \ def __set__(self, object value): \ CHECK_LOOP3(self.loop) \ if value: \ if not self._flags & 4: \ return # ref is already True \ if self._flags & 2: # ev_unref was called, undo \ libev.ev_ref(self.loop._ptr) \ self._flags &= ~6 # do not want unref, no outstanding unref \ else: \ if self._flags & 4: \ return # ref is already False \ self._flags |= 4 \ if not self._flags & 2 and libev.ev_is_active(&self._watcher): \ libev.ev_unref(self.loop._ptr) \ self._flags |= 2 \ \ property callback: \ \ def __get__(self): \ return self._callback \ \ def __set__(self, object callback): \ if not PyCallable_Check(callback) and callback is not None: \ raise TypeError("Expected callable, not %r" % (callback, )) \ self._callback = callback \ \ def stop(self): \ CHECK_LOOP2(self.loop) \ if self._flags & 2: \ libev.ev_ref(self.loop._ptr) \ self._flags &= ~2 \ libev.ev_##TYPE##_stop(self.loop._ptr, &self._watcher) \ self._callback = None \ self.args = None \ if self._flags & 1: \ Py_DECREF(self) \ self._flags &= ~1 \ \ property priority: \ \ def __get__(self): \ return libev.ev_priority(&self._watcher) \ \ def __set__(self, int priority): \ if libev.ev_is_active(&self._watcher): \ raise AttributeError("Cannot set priority of an active watcher") \ libev.ev_set_priority(&self._watcher, priority) \ \ def feed(self, int revents, object callback, *args): \ CHECK_LOOP2(self.loop) \ self.callback = callback \ self.args = args \ LIBEV_UNREF \ libev.ev_feed_event(self.loop._ptr, &self._watcher, revents) \ PYTHON_INCREF #define ACTIVE property active: \ \ def __get__(self): \ return True if libev.ev_is_active(&self._watcher) else False #define START(TYPE) def start(self, object callback, *args): \ CHECK_LOOP2(self.loop) \ if callback is None: \ raise TypeError('callback must be callable, not None') \ self.callback = callback \ self.args = args \ LIBEV_UNREF \ libev.ev_##TYPE##_start(self.loop._ptr, &self._watcher) \ PYTHON_INCREF #define PENDING \ property pending: \ \ def __get__(self): \ return True if libev.ev_is_pending(&self._watcher) else False #define WATCHER(TYPE) WATCHER_BASE(TYPE) \ \ START(TYPE) \ \ ACTIVE \ \ PENDING #define COMMA , #define INIT(TYPE, ARGS_INITIALIZERS, ARGS) \ def __init__(self, loop loop ARGS_INITIALIZERS, ref=True, priority=None): \ libev.ev_##TYPE##_init(&self._watcher, gevent_callback_##TYPE ARGS) \ self.loop = loop \ if ref: \ self._flags = 0 \ else: \ self._flags = 4 \ if priority is not None: \ libev.ev_set_priority(&self._watcher, priority) cdef public class watcher [object PyGeventWatcherObject, type PyGeventWatcher_Type]: """Abstract base class for all the watchers""" def __repr__(self): if Py_ReprEnter(self) != 0: return "<...>" try: format = self._format() result = "<%s at 0x%x%s" % (self.__class__.__name__, id(self), format) if self.active: result += " active" if self.pending: result += " pending" if self.callback is not None: result += " callback=%r" % (self.callback, ) if self.args is not None: result += " args=%r" % (self.args, ) return result + ">" finally: Py_ReprLeave(self) def _format(self): return '' cdef public class io(watcher) [object PyGeventIOObject, type PyGeventIO_Type]: WATCHER_BASE(io) def start(self, object callback, *args, pass_events=False): CHECK_LOOP2(self.loop) if callback is None: raise TypeError('callback must be callable, not None') self.callback = callback if pass_events: self.args = (GEVENT_CORE_EVENTS, ) + args else: self.args = args LIBEV_UNREF libev.ev_io_start(self.loop._ptr, &self._watcher) PYTHON_INCREF ACTIVE PENDING #ifdef _WIN32 def __init__(self, loop loop, libev.vfd_socket_t fd, int events, ref=True, priority=None): if events & ~(libev.EV__IOFDSET | libev.EV_READ | libev.EV_WRITE): raise ValueError('illegal event mask: %r' % events) cdef int vfd = libev.vfd_open(fd) libev.vfd_free(self._watcher.fd) libev.ev_io_init(&self._watcher, gevent_callback_io, vfd, events) self.loop = loop if ref: self._flags = 0 else: self._flags = 4 if priority is not None: libev.ev_set_priority(&self._watcher, priority) #else def __init__(self, loop loop, int fd, int events, ref=True, priority=None): if fd < 0: raise ValueError('fd must be non-negative: %r' % fd) if events & ~(libev.EV__IOFDSET | libev.EV_READ | libev.EV_WRITE): raise ValueError('illegal event mask: %r' % events) libev.ev_io_init(&self._watcher, gevent_callback_io, fd, events) self.loop = loop if ref: self._flags = 0 else: self._flags = 4 if priority is not None: libev.ev_set_priority(&self._watcher, priority) #endif property fd: def __get__(self): return libev.vfd_get(self._watcher.fd) def __set__(self, long fd): if libev.ev_is_active(&self._watcher): raise AttributeError("'io' watcher attribute 'fd' is read-only while watcher is active") cdef int vfd = libev.vfd_open(fd) libev.vfd_free(self._watcher.fd) libev.ev_io_init(&self._watcher, gevent_callback_io, vfd, self._watcher.events) property events: def __get__(self): return self._watcher.events def __set__(self, int events): if libev.ev_is_active(&self._watcher): raise AttributeError("'io' watcher attribute 'events' is read-only while watcher is active") libev.ev_io_init(&self._watcher, gevent_callback_io, self._watcher.fd, events) property events_str: def __get__(self): return _events_to_str(self._watcher.events) def _format(self): return ' fd=%s events=%s' % (self.fd, self.events_str) #ifdef _WIN32 def __cinit__(self): self._watcher.fd = -1; def __dealloc__(self): libev.vfd_free(self._watcher.fd) #endif cdef public class timer(watcher) [object PyGeventTimerObject, type PyGeventTimer_Type]: WATCHER_BASE(timer) def start(self, object callback, *args, update=True): CHECK_LOOP2(self.loop) if callback is None: raise TypeError('callback must be callable, not None') self.callback = callback self.args = args LIBEV_UNREF if update: libev.ev_now_update(self.loop._ptr) libev.ev_timer_start(self.loop._ptr, &self._watcher) PYTHON_INCREF ACTIVE PENDING def __init__(self, loop loop, double after=0.0, double repeat=0.0, ref=True, priority=None): if repeat < 0.0: raise ValueError("repeat must be positive or zero: %r" % repeat) libev.ev_timer_init(&self._watcher, gevent_callback_timer, after, repeat) self.loop = loop if ref: self._flags = 0 else: self._flags = 4 if priority is not None: libev.ev_set_priority(&self._watcher, priority) property at: def __get__(self): return self._watcher.at # QQQ: add 'after' and 'repeat' properties? def again(self, object callback, *args, update=True): CHECK_LOOP2(self.loop) self.callback = callback self.args = args LIBEV_UNREF if update: libev.ev_now_update(self.loop._ptr) libev.ev_timer_again(self.loop._ptr, &self._watcher) PYTHON_INCREF cdef public class signal(watcher) [object PyGeventSignalObject, type PyGeventSignal_Type]: WATCHER(signal) def __init__(self, loop loop, int signalnum, ref=True, priority=None): if signalnum < 1 or signalnum >= signalmodule.NSIG: raise ValueError('illegal signal number: %r' % signalnum) # still possible to crash on one of libev's asserts: # 1) "libev: ev_signal_start called with illegal signal number" # EV_NSIG might be different from signal.NSIG on some platforms # 2) "libev: a signal must not be attached to two different loops" # we probably could check that in LIBEV_EMBED mode, but not in general libev.ev_signal_init(&self._watcher, gevent_callback_signal, signalnum) self.loop = loop if ref: self._flags = 0 else: self._flags = 4 if priority is not None: libev.ev_set_priority(&self._watcher, priority) cdef public class idle(watcher) [object PyGeventIdleObject, type PyGeventIdle_Type]: WATCHER(idle) INIT(idle,,) cdef public class prepare(watcher) [object PyGeventPrepareObject, type PyGeventPrepare_Type]: WATCHER(prepare) INIT(prepare,,) cdef public class check(watcher) [object PyGeventCheckObject, type PyGeventCheck_Type]: WATCHER(check) INIT(check,,) cdef public class fork(watcher) [object PyGeventForkObject, type PyGeventFork_Type]: WATCHER(fork) INIT(fork,,) cdef public class async(watcher) [object PyGeventAsyncObject, type PyGeventAsync_Type]: WATCHER_BASE(async) START(async) ACTIVE property pending: def __get__(self): return True if libev.ev_async_pending(&self._watcher) else False INIT(async,,) def send(self): CHECK_LOOP2(self.loop) libev.ev_async_send(self.loop._ptr, &self._watcher) #ifdef _WIN32 #else cdef public class child(watcher) [object PyGeventChildObject, type PyGeventChild_Type]: WATCHER(child) def __init__(self, loop loop, int pid, bint trace=0, ref=True): if not loop.default: raise TypeError('child watchers are only available on the default loop') libev.gevent_install_sigchld_handler() libev.ev_child_init(&self._watcher, gevent_callback_child, pid, trace) self.loop = loop if ref: self._flags = 0 else: self._flags = 4 def _format(self): return ' pid=%r rstatus=%r' % (self.pid, self.rstatus) property pid: def __get__(self): return self._watcher.pid property rpid: def __get__(self): return self._watcher.rpid def __set__(self, int value): self._watcher.rpid = value property rstatus: def __get__(self): return self._watcher.rstatus def __set__(self, int value): self._watcher.rstatus = value #endif cdef public class stat(watcher) [object PyGeventStatObject, type PyGeventStat_Type]: WATCHER(stat) cdef readonly str path cdef readonly bytes _paths def __init__(self, loop loop, str path, float interval=0.0, ref=True, priority=None): self.path = path cdef bytes paths if isinstance(path, unicode): # the famous Python3 filesystem encoding debacle hits us here. Can we do better? # We must keep a reference to the encoded string so that its bytes don't get freed # and overwritten, leading to strange errors from libev ("no such file or directory") paths = (path).encode(sys.getfilesystemencoding()) self._paths = paths else: paths = path self._paths = paths libev.ev_stat_init(&self._watcher, gevent_callback_stat, paths, interval) self.loop = loop if ref: self._flags = 0 else: self._flags = 4 if priority is not None: libev.ev_set_priority(&self._watcher, priority) property attr: def __get__(self): if not self._watcher.attr.st_nlink: return return _pystat_fromstructstat(&self._watcher.attr) property prev: def __get__(self): if not self._watcher.prev.st_nlink: return return _pystat_fromstructstat(&self._watcher.prev) property interval: def __get__(self): return self._watcher.interval __SYSERR_CALLBACK = None cdef void _syserr_cb(char* msg) with gil: try: __SYSERR_CALLBACK(msg, errno) except: set_syserr_cb(None) print_exc = getattr(traceback, 'print_exc', None) if print_exc is not None: print_exc() cpdef set_syserr_cb(callback): global __SYSERR_CALLBACK if callback is None: libev.ev_set_syserr_cb(NULL) __SYSERR_CALLBACK = None elif callable(callback): libev.ev_set_syserr_cb(_syserr_cb) __SYSERR_CALLBACK = callback else: raise TypeError('Expected callable or None, got %r' % (callback, )) #ifdef LIBEV_EMBED LIBEV_EMBED = True EV_USE_FLOOR = libev.EV_USE_FLOOR EV_USE_CLOCK_SYSCALL = libev.EV_USE_CLOCK_SYSCALL EV_USE_REALTIME = libev.EV_USE_REALTIME EV_USE_MONOTONIC = libev.EV_USE_MONOTONIC EV_USE_NANOSLEEP = libev.EV_USE_NANOSLEEP EV_USE_INOTIFY = libev.EV_USE_INOTIFY EV_USE_SIGNALFD = libev.EV_USE_SIGNALFD EV_USE_EVENTFD = libev.EV_USE_EVENTFD EV_USE_4HEAP = libev.EV_USE_4HEAP #else LIBEV_EMBED = False #endif