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