/pyevent/event.pyx

https://code.google.com/p/pyevent/ · Cython · 280 lines · 218 code · 51 blank · 11 comment · 40 complexity · 4ddd418df10e6fe0d6e841618a66fed5 MD5 · raw file

  1. #
  2. # event.pyx
  3. #
  4. # libevent Python bindings
  5. #
  6. # Copyright (c) 2004 Dug Song <dugsong@monkey.org>
  7. # Copyright (c) 2003 Martin Murray <murrayma@citi.umich.edu>
  8. #
  9. # $Id$
  10. """event library
  11. This module provides a mechanism to execute a function when a
  12. specific event on a file handle, file descriptor, or signal occurs,
  13. or after a given time has passed.
  14. """
  15. __author__ = ( 'Dug Song <dugsong@monkey.org>',
  16. 'Martin Murray <mmurray@monkey.org>' )
  17. __copyright__ = ( 'Copyright (c) 2004 Dug Song',
  18. 'Copyright (c) 2003 Martin Murray' )
  19. __license__ = 'BSD'
  20. __url__ = 'http://monkey.org/~dugsong/pyevent/'
  21. __version__ = '0.3'
  22. import sys
  23. cdef extern from "Python.h":
  24. void Py_INCREF(object o)
  25. void Py_DECREF(object o)
  26. ctypedef void (*event_handler)(int fd, short evtype, void *arg)
  27. cdef extern from "event.h":
  28. struct timeval:
  29. unsigned int tv_sec
  30. unsigned int tv_usec
  31. struct event_t "event":
  32. int ev_fd
  33. int ev_flags
  34. void *ev_arg
  35. void event_init()
  36. void event_set(event_t *ev, int fd, short event,
  37. event_handler handler, void *arg)
  38. void evtimer_set(event_t *ev, event_handler handler, void *arg)
  39. int event_add(event_t *ev, timeval *tv)
  40. int event_del(event_t *ev)
  41. int event_dispatch()
  42. int event_loop(int loop)
  43. int event_pending(event_t *ev, short, timeval *tv)
  44. int EVLOOP_ONCE
  45. int EVLOOP_NONBLOCK
  46. EV_TIMEOUT = 0x01
  47. EV_READ = 0x02
  48. EV_WRITE = 0x04
  49. EV_SIGNAL = 0x08
  50. EV_PERSIST = 0x10
  51. __event_exc = None
  52. cdef int __event_sigcb():
  53. return -1
  54. cdef void __event_handler(int fd, short evtype, void *arg):
  55. (<object>arg).__callback(evtype)
  56. cdef void __simple_event_handler(int fd, short evtype, void *arg):
  57. (<object>arg).__simple_callback(evtype)
  58. cdef class event:
  59. """event(callback, arg=None, evtype=0, handle=None) -> event object
  60. Create a new event object with a user callback.
  61. Arguments:
  62. callback -- user callback with (ev, handle, evtype, arg) prototype
  63. arg -- optional callback arguments
  64. evtype -- bitmask of EV_READ or EV_WRITE, or EV_SIGNAL
  65. handle -- for EV_READ or EV_WRITE, a file handle, descriptor, or socket
  66. for EV_SIGNAL, a signal number
  67. """
  68. cdef event_t ev
  69. cdef object handle, evtype, callback, args
  70. cdef float timeout
  71. cdef timeval tv
  72. def __init__(self, callback, arg=None, short evtype=0, handle=-1,
  73. simple=0):
  74. cdef event_handler handler
  75. self.callback = callback
  76. self.args = arg
  77. self.evtype = evtype
  78. self.handle = handle
  79. if simple:
  80. handler = __simple_event_handler
  81. else:
  82. handler = __event_handler
  83. if evtype == 0 and not handle:
  84. evtimer_set(&self.ev, handler, <void *>self)
  85. else:
  86. if not isinstance(handle, int):
  87. handle = handle.fileno()
  88. event_set(&self.ev, handle, evtype, handler, <void *>self)
  89. def __simple_callback(self, short evtype):
  90. cdef extern int event_gotsig
  91. cdef extern int (*event_sigcb)()
  92. global __event_exc
  93. try:
  94. if self.callback(*self.args) != None:
  95. if self.tv.tv_sec or self.tv.tv_usec:
  96. event_add(&self.ev, &self.tv)
  97. else:
  98. event_add(&self.ev, NULL)
  99. except:
  100. __event_exc = sys.exc_info()
  101. event_sigcb = __event_sigcb
  102. event_gotsig = 1
  103. # XXX - account for event.signal() EV_PERSIST
  104. if not (evtype & EV_SIGNAL) and \
  105. not event_pending(&self.ev, EV_READ|EV_WRITE|EV_SIGNAL|EV_TIMEOUT, NULL):
  106. Py_DECREF(self)
  107. def __callback(self, short evtype):
  108. cdef extern int event_gotsig
  109. cdef extern int (*event_sigcb)()
  110. global __event_exc
  111. try:
  112. self.callback(self, self.handle, evtype, self.args)
  113. except:
  114. __event_exc = sys.exc_info()
  115. event_sigcb = __event_sigcb
  116. event_gotsig = 1
  117. if not event_pending(&self.ev, EV_READ|EV_WRITE|EV_SIGNAL|EV_TIMEOUT, NULL):
  118. Py_DECREF(self)
  119. def add(self, float timeout=-1):
  120. """Add event to be executed after an optional timeout.
  121. Arguments:
  122. timeout -- seconds after which the event will be executed
  123. """
  124. if not event_pending(&self.ev, EV_READ|EV_WRITE|EV_SIGNAL|EV_TIMEOUT,
  125. NULL):
  126. Py_INCREF(self)
  127. self.timeout = timeout
  128. if timeout >= 0.0:
  129. self.tv.tv_sec = <long>timeout
  130. self.tv.tv_usec = (timeout - <float>self.tv.tv_sec) * 1000000.0
  131. event_add(&self.ev, &self.tv)
  132. else:
  133. self.tv.tv_sec = self.tv.tv_usec = 0
  134. event_add(&self.ev, NULL)
  135. def pending(self):
  136. """Return 1 if the event is scheduled to run, or else 0."""
  137. return event_pending(&self.ev, EV_TIMEOUT|EV_SIGNAL|EV_READ|EV_WRITE, NULL)
  138. def delete(self):
  139. """Remove event from the event queue."""
  140. if self.pending():
  141. event_del(&self.ev)
  142. Py_DECREF(self)
  143. def __dealloc__(self):
  144. self.delete()
  145. def __repr__(self):
  146. return '<event flags=0x%x, handle=%s, callback=%s, arg=%s>' % \
  147. (self.ev.ev_flags, self.handle, self.callback, self.args)
  148. cdef class read(event):
  149. """read(handle, callback, *args) -> event object
  150. Simplified event interface:
  151. Create a new read event, and add it to the event queue.
  152. Arguments:
  153. handle -- file handle, descriptor, or socket
  154. callback -- user callback with (*args) prototype, which can return
  155. a non-None value to be rescheduled
  156. *args -- optional callback arguments
  157. """
  158. def __init__(self, handle, callback, *args):
  159. event.__init__(self, callback, args, EV_READ, handle, simple=1)
  160. self.args = args # XXX - incref
  161. self.add()
  162. cdef class write(event):
  163. """write(handle, callback, *args) -> event object
  164. Simplified event interface:
  165. Create a new write event, and add it to the event queue.
  166. Arguments:
  167. handle -- file handle, descriptor, or socket
  168. callback -- user callback with (*args) prototype, which can return
  169. a non-None value to be rescheduled
  170. *args -- optional callback arguments
  171. """
  172. def __init__(self, handle, callback, *args):
  173. event.__init__(self, callback, args, EV_WRITE, handle, simple=1)
  174. self.args = args # XXX - incref
  175. self.add()
  176. cdef class signal(event):
  177. """signal(sig, callback, *args) -> event object
  178. Simplified event interface:
  179. Create a new signal event, and add it to the event queue.
  180. XXX - persistent event is added with EV_PERSIST, like signal_set()
  181. Arguments:
  182. sig -- signal number
  183. callback -- user callback with (*args) prototype, which can return
  184. a non-None value to be rescheduled
  185. *args -- optional callback arguments
  186. """
  187. def __init__(self, sig, callback, *args):
  188. event.__init__(self, callback, args, EV_SIGNAL|EV_PERSIST,
  189. sig, simple=1)
  190. self.args = args # XXX - incref
  191. self.add()
  192. cdef class timeout(event):
  193. """timeout(secs, callback, *args) -> event object
  194. Simplified event interface:
  195. Create a new timer event, and add it to the event queue.
  196. Arguments:
  197. secs -- event timeout in seconds
  198. callback -- user callback with (*args) prototype, which can return
  199. a non-None value to be rescheduled
  200. *args -- optional callback arguments
  201. """
  202. def __init__(self, secs, callback, *args):
  203. event.__init__(self, callback, args, simple=1)
  204. self.args = args # XXX - incref
  205. self.add(secs)
  206. def init():
  207. """Initialize event queue."""
  208. event_init()
  209. def dispatch():
  210. """Dispatch all events on the event queue."""
  211. global __event_exc
  212. event_dispatch()
  213. if __event_exc:
  214. raise __event_exc[0], __event_exc[1], __event_exc[2]
  215. def loop(nonblock=False):
  216. """Dispatch all pending events on queue in a single pass."""
  217. cdef int flags
  218. flags = EVLOOP_ONCE
  219. if nonblock:
  220. flags = EVLOOP_ONCE|EVLOOP_NONBLOCK
  221. event_loop(flags)
  222. def abort():
  223. """Abort event dispatch loop."""
  224. cdef extern int event_gotsig
  225. cdef extern int (*event_sigcb)()
  226. event_sigcb = __event_sigcb
  227. event_gotsig = 1
  228. # XXX - make sure event queue is always initialized.
  229. init()