]> git.llucax.com Git - software/pymin.git/blob - eventloop.py
acc370ab03b439730d3eb242d50d6985dc13b349
[software/pymin.git] / eventloop.py
1 # vim: set encoding=utf-8 et sw=4 sts=4 :
2
3 r"""
4 A simple event loop.
5
6 Please see EventLoop class documentation for more info.
7 """
8
9 import select
10 from select import POLLIN, POLLPRI, POLLERR
11
12 __ALL__ = ('EventLoop')
13
14 class LoopInterruptedError(RuntimeError):
15     def __init__(self, select_error):
16         self.select_error = select_error
17     def __repr__(self):
18         return 'LoopInterruptedError(select_error=%r)' % self.select_error
19     def __str__(self):
20         return 'Loop interrupted: %s' % self.select_error
21
22 class EventLoop:
23     r"""EventLoop(file[, handler]) -> EventLoop instance
24
25     This class implements a simple event loop based on select module.
26     It "listens" to activity a single 'file' object (a file, a pipe,
27     a socket, or even a simple file descriptor) and calls a 'handler'
28     function (or the handle() method if you prefer subclassing) every
29     time the file is ready for reading (or has an error).
30
31     This is a really simple example of usage using a hanlder callable:
32
33     >>> import os 
34     >>> def handle(event_loop):
35             data = os.read(event_loop.fileno, 100)
36             os.write(1, 'Received message: %r\n' % data)
37     >>> p = EventLoop(0, handle)
38     >>> p.loop(once=True)
39
40     In this example only one event is handled (see the 'once' argument
41     of loop).
42
43     A more complex example, making a subclass and explicitly stopping
44     the loop, looks something like this:
45
46     >>> class Test(EventLoop):
47     >>>     def handle(self):
48     >>>         data = os.read(self.fileno, 100)
49     >>>         if data == 'q\n':
50     >>>             self.stop()
51     >>>         else:
52     >>>             os.write(1, 'Received message: %r\n' % data)
53     >>> p = Test(0)
54     >>> p.loop()
55
56     This example loops until the user enters a single "q", when stop()
57     is called and the event loop is exited.
58     """
59
60     def __init__(self, file, handler=None):
61         r"""Initialize the EventLoop object.
62
63         See EventLoop class documentation for more info.
64         """
65         self.poll = select.poll()
66         self._stop = False
67         self.__register(file)
68         self.handler = handler
69
70     def __register(self, file):
71         r"__register(file) -> None :: Register a new file for polling."
72         self._file = file
73         self.poll.register(self.fileno, POLLIN | POLLPRI | POLLERR)
74
75     def set_file(self, file):
76         r"""set_file(file) -> None :: New file object to be monitored
77
78         Unregister the previous file object being monitored and register
79         a new one.
80         """
81         self.poll.unregister(self.fileno)
82         self.__register(file)
83
84     def get_file(self):
85         r"get_file() -> file object/int :: Get the current file object/fd."
86         return self._file
87
88     file = property(get_file, set_file, doc='File object (or descriptor)')
89
90     def get_fileno(self):
91         r"get_fileno() -> int :: Get the current file descriptor"
92         if hasattr(self.file, 'fileno'):
93             return self.file.fileno()
94         return self.file
95
96     fileno = property(get_fileno, doc='File descriptor (never a file object)')
97
98     def stop(self):
99         r"""stop() -> None :: Stop the event loop.
100
101         The event loop will be interrupted as soon as the current handler
102         finishes.
103         """
104         self._stop = True
105
106     def loop(self, once=False):
107         r"""loop([once]) -> None :: Wait for events.
108
109         Wait for events and handle then when they arrive. If once is True,
110         then only 1 event is processed and then this method returns.
111         """
112         while True:
113             try:
114                 res = self.poll.poll()
115             except select.error, e:
116                 raise LoopInterruptedError(e)
117             if self.handler is not None:
118                 self.handler(self)
119             else:
120                 self.handle()
121             if self._stop or once:
122                 self._stop = False
123                 break
124
125     def handle(self):
126         r"handle() -> None :: Abstract method to be overriden to handle events."
127         raise NotImplementedError
128
129 if __name__ == '__main__':
130
131     import os
132
133     def handle(event_loop):
134         data = os.read(event_loop.fileno, 100)
135         os.write(1, 'Received message: %r\n' % data)
136
137     p = EventLoop(0, handle)
138
139     os.write(1, 'Say something once: ')
140     p.loop(once=True)
141     os.write(1, 'Great!\n')
142
143     class Test(EventLoop):
144         def handle(self):
145             data = os.read(self.fileno, 100)
146             if data == 'q\n':
147                 self.stop()
148             else:
149                 os.write(1, 'Received message: %r\n' % data)
150
151     p = Test(0)
152
153     os.write(1, 'Say a lot of things, then press write just "q" to stop: ')
154     p.loop()
155     os.write(1, 'Ok, bye!\n')
156