1 # vim: set encoding=utf-8 et sw=4 sts=4 :
6 Please see EventLoop class documentation for more info.
12 from select import POLLIN, POLLPRI, POLLERR
13 import logging ; log = logging.getLogger('pymin.eventloop')
15 __ALL__ = ('EventLoop', 'LoopInterruptedError')
17 class LoopInterruptedError(RuntimeError):
19 LoopInterruptedError(select_error) -> LoopInterruptedError instance.
21 This class is raised when the event loop is interrupted in an unexpected
22 way. It wraps a select error, which can be accessed using the 'select_error'
26 def __init__(self, select_error):
27 r"""Initialize the object.
29 See the class documentation for more info.
31 self.select_error = select_error
34 r"repr(obj) -> Object representation."
35 return 'LoopInterruptedError(select_error=%r)' % self.select_error
38 r"str(obj) -> String representation."
39 return 'Loop interrupted: %s' % self.select_error
41 # Flag to know if a timer was expired
44 # Alarm Signal handler
45 def alarm_handler(signum, stack_frame):
50 r"""EventLoop(file[, timer[, handler[, timer_handler]]]) -> EventLoop.
52 This class implements a simple event loop based on select module.
53 It "listens" to activity a single 'file' object (a file, a pipe,
54 a socket, or even a simple file descriptor) and calls a 'handler'
55 function (or the handle() method if you prefer subclassing) every
56 time the file is ready for reading (or has an error).
58 If a 'timer' is supplied, then the timer_handler() function object
59 (or the handle_timer() method) is called every 'timer' seconds.
61 This is a really simple example of usage using a hanlder callable:
64 >>> def handle(event_loop):
65 data = os.read(event_loop.fileno, 100)
66 os.write(1, 'Received message: %r\n' % data)
67 >>> p = EventLoop(0, handle)
70 In this example only one event is handled (see the 'once' argument
73 A more complex example, making a subclass and explicitly stopping
74 the loop, looks something like this:
76 >>> class Test(EventLoop):
78 >>> data = os.read(self.fileno, 100)
82 >>> os.write(1, 'Received message: %r\n' % data)
83 >>> def handle_timer(self):
84 >>> print time.strftime('%c')
85 >>> p = Test(0, timer=5)
88 This example loops until the user enters a single "q", when stop()
89 is called and the event loop is exited.
92 def __init__(self, file, handler=None, timer=None, timer_handler=None):
93 r"""Initialize the EventLoop object.
95 See EventLoop class documentation for more info.
97 log.debug(u'EventLoop(%r, %r, %r, %r)', file, handler,
99 self.poll = select.poll()
101 self.__register(file)
103 self.handler = handler
104 self.timer_handler = timer_handler
106 def __register(self, file):
107 r"__register(file) -> None :: Register a new file for polling."
109 self.poll.register(self.fileno, POLLIN | POLLPRI | POLLERR)
111 def set_file(self, file):
112 r"""set_file(file) -> None :: New file object to be monitored
114 Unregister the previous file object being monitored and register
117 self.poll.unregister(self.fileno)
118 self.__register(file)
121 r"get_file() -> file object/int :: Get the current file object/fd."
124 file = property(get_file, set_file, doc='File object (or descriptor)')
126 def get_fileno(self):
127 r"get_fileno() -> int :: Get the current file descriptor"
128 if hasattr(self.file, 'fileno'):
129 return self.file.fileno()
132 fileno = property(get_fileno, doc='File descriptor (never a file object)')
135 r"""stop() -> None :: Stop the event loop.
137 The event loop will be interrupted as soon as the current handler
140 log.debug(u'EventLoop.stop()')
143 def loop(self, once=False):
144 r"""loop([once]) -> None :: Wait for events.
146 Wait for events and handle then when they arrive. If once is True,
147 then only 1 event is processed and then this method returns.
149 log.debug(u'EventLoop.loop(%s)', once)
150 # Flag modified by the signal handler
152 # If we use a timer, we set up the signal
153 if self.timer is not None:
154 signal.signal(signal.SIGALRM, alarm_handler)
156 signal.alarm(self.timer)
159 log.debug(u'EventLoop.loop: polling')
160 res = self.poll.poll()
161 except select.error, e:
162 # The error is not an interrupt caused by the alarm, then raise
163 if e.args[0] != errno.EINTR or not timeout:
164 raise LoopInterruptedError(e)
165 # There was a timeout, so execute the timer handler
167 log.debug(u'EventLoop.loop: timer catched, handling...')
170 signal.alarm(self.timer)
171 # Not a timeout, execute the regular handler
173 log.debug(u'EventLoop.loop: no timeout, handle event')
175 # Look if we have to stop
176 if self._stop or once:
177 log.debug(u'EventLoop.loop: stopped')
182 r"handle() -> None :: Abstract method to be overriden to handle events."
185 def handle_timer(self):
186 r"handle() -> None :: Abstract method to be overriden to handle events."
187 self.timer_handler(self)
189 if __name__ == '__main__':
192 level = logging.DEBUG,
193 format = '%(asctime)s %(levelname)-8s %(message)s',
194 datefmt = '%H:%M:%S',
200 def handle(event_loop):
201 data = os.read(event_loop.fileno, 100)
202 os.write(1, 'Received message: %r\n' % data)
204 p = EventLoop(0, handle)
206 os.write(1, 'Say something once: ')
208 os.write(1, 'Great!\n')
210 class Test(EventLoop):
212 data = os.read(self.fileno, 100)
216 os.write(1, 'Received message: %r\n' % data)
217 def handle_timer(self):
218 print time.strftime('%c')
222 os.write(1, 'Say a lot of things, then press write just "q" to stop: ')
224 os.write(1, 'Ok, bye!\n')