]> git.llucax.com Git - software/pymin.git/commitdiff
Add support for a simple timer to do periodic tasks.
authorLeandro Lucarella <llucax@gmail.com>
Wed, 24 Oct 2007 20:41:28 +0000 (17:41 -0300)
committerLeandro Lucarella <llucax@gmail.com>
Wed, 24 Oct 2007 20:41:28 +0000 (17:41 -0300)
Base support is on EventLoop, who handles the timer
life and calls handle_timer() when the timer is
expired. dispatcher.Handler has a simple default
handle_timer() implementation that just promote the
"handle_timer message" to all subhandlers, while
PyDaemon just start "spreading the voice" by calling
the root handler handle_timer() method.

pymin/dispatcher.py
pymin/eventloop.py
pymin/pymindaemon.py

index 17075a3f1dfec8617432dd68fb532022f7299b2b..24f0d1f14e6a52b6f97fd49664d767b07382a99a 100644 (file)
@@ -216,6 +216,17 @@ class Handler:
             raise HelpNotFoundError(command)
         return handler.handler_help
 
             raise HelpNotFoundError(command)
         return handler.handler_help
 
+    def handle_timer(self):
+        r"""handle_timer() -> None :: Do periodic tasks.
+
+        By default we do nothing but calling handle_timer() on subhandlers.
+        """
+        for a in dir(self):
+            if a == 'parent': continue # Skip parents in SubHandlers
+            h = getattr(self, a)
+            if isinstance(h, Handler):
+                h.handle_timer()
+
 def parse_command(command):
     r"""parse_command(command) -> (args, kwargs) :: Parse a command.
 
 def parse_command(command):
     r"""parse_command(command) -> (args, kwargs) :: Parse a command.
 
index 06922a72fe9290bcfb5133700e49ae6187d0dc3c..b6be5a1003d891b7e857a166b9523647de3a3005 100644 (file)
@@ -7,6 +7,8 @@ Please see EventLoop class documentation for more info.
 """
 
 import select
 """
 
 import select
+import errno
+import signal
 from select import POLLIN, POLLPRI, POLLERR
 
 __ALL__ = ('EventLoop', 'LoopInterruptedError')
 from select import POLLIN, POLLPRI, POLLERR
 
 __ALL__ = ('EventLoop', 'LoopInterruptedError')
@@ -35,8 +37,16 @@ class LoopInterruptedError(RuntimeError):
         r"str(obj) -> String representation."
         return 'Loop interrupted: %s' % self.select_error
 
         r"str(obj) -> String representation."
         return 'Loop interrupted: %s' % self.select_error
 
+# Flag to know if a timer was expired
+timeout = False
+
+# Alarm Signal handler
+def alarm_handler(signum, stack_frame):
+    global timeout
+    timeout = True
+
 class EventLoop:
 class EventLoop:
-    r"""EventLoop(file[, handler]) -> EventLoop instance
+    r"""EventLoop(file[, timer[, handler[, timer_handler]]]) -> EventLoop.
 
     This class implements a simple event loop based on select module.
     It "listens" to activity a single 'file' object (a file, a pipe,
 
     This class implements a simple event loop based on select module.
     It "listens" to activity a single 'file' object (a file, a pipe,
@@ -44,9 +54,12 @@ class EventLoop:
     function (or the handle() method if you prefer subclassing) every
     time the file is ready for reading (or has an error).
 
     function (or the handle() method if you prefer subclassing) every
     time the file is ready for reading (or has an error).
 
+    If a 'timer' is supplied, then the timer_handler() function object
+    (or the handle_timer() method) is called every 'timer' seconds.
+
     This is a really simple example of usage using a hanlder callable:
 
     This is a really simple example of usage using a hanlder callable:
 
-    >>> import os 
+    >>> import os
     >>> def handle(event_loop):
             data = os.read(event_loop.fileno, 100)
             os.write(1, 'Received message: %r\n' % data)
     >>> def handle(event_loop):
             data = os.read(event_loop.fileno, 100)
             os.write(1, 'Received message: %r\n' % data)
@@ -66,14 +79,16 @@ class EventLoop:
     >>>             self.stop()
     >>>         else:
     >>>             os.write(1, 'Received message: %r\n' % data)
     >>>             self.stop()
     >>>         else:
     >>>             os.write(1, 'Received message: %r\n' % data)
-    >>> p = Test(0)
+    >>>     def handle_timer(self):
+    >>>         print time.strftime('%c')
+    >>> p = Test(0, timer=5)
     >>> p.loop()
 
     This example loops until the user enters a single "q", when stop()
     is called and the event loop is exited.
     """
 
     >>> p.loop()
 
     This example loops until the user enters a single "q", when stop()
     is called and the event loop is exited.
     """
 
-    def __init__(self, file, handler=None):
+    def __init__(self, file, handler=None, timer=None, timer_handler=None):
         r"""Initialize the EventLoop object.
 
         See EventLoop class documentation for more info.
         r"""Initialize the EventLoop object.
 
         See EventLoop class documentation for more info.
@@ -81,7 +96,9 @@ class EventLoop:
         self.poll = select.poll()
         self._stop = False
         self.__register(file)
         self.poll = select.poll()
         self._stop = False
         self.__register(file)
+        self.timer = timer
         self.handler = handler
         self.handler = handler
+        self.timer_handler = timer_handler
 
     def __register(self, file):
         r"__register(file) -> None :: Register a new file for polling."
 
     def __register(self, file):
         r"__register(file) -> None :: Register a new file for polling."
@@ -125,26 +142,44 @@ class EventLoop:
         Wait for events and handle then when they arrive. If once is True,
         then only 1 event is processed and then this method returns.
         """
         Wait for events and handle then when they arrive. If once is True,
         then only 1 event is processed and then this method returns.
         """
+        # Flag modified by the signal handler
+        global timeout
+        # If we use a timer, we set up the signal
+        if self.timer is not None:
+            signal.signal(signal.SIGALRM, alarm_handler)
+            signal.alarm(self.timer)
         while True:
             try:
                 res = self.poll.poll()
             except select.error, e:
         while True:
             try:
                 res = self.poll.poll()
             except select.error, e:
-                raise LoopInterruptedError(e)
-            if self.handler is not None:
-                self.handler(self)
+                # The error is not an interrupt caused by the alarm, then raise
+                if e.args[0] != errno.EINTR or not timeout:
+                    raise LoopInterruptedError(e)
+            # There was a timeout, so execute the timer handler
+            if timeout:
+                timeout = False
+                self.handle_timer()
+                signal.alarm(self.timer)
+            # Not a timeout, execute the regular handler
             else:
                 self.handle()
             else:
                 self.handle()
+            # Look if we have to stop
             if self._stop or once:
                 self._stop = False
                 break
 
     def handle(self):
         r"handle() -> None :: Abstract method to be overriden to handle events."
             if self._stop or once:
                 self._stop = False
                 break
 
     def handle(self):
         r"handle() -> None :: Abstract method to be overriden to handle events."
-        raise NotImplementedError
+        self.handler(self)
+
+    def handle_timer(self):
+        r"handle() -> None :: Abstract method to be overriden to handle events."
+        self.timer_handler(self)
 
 if __name__ == '__main__':
 
     import os
 
 if __name__ == '__main__':
 
     import os
+    import time
 
     def handle(event_loop):
         data = os.read(event_loop.fileno, 100)
 
     def handle(event_loop):
         data = os.read(event_loop.fileno, 100)
@@ -163,8 +198,10 @@ if __name__ == '__main__':
                 self.stop()
             else:
                 os.write(1, 'Received message: %r\n' % data)
                 self.stop()
             else:
                 os.write(1, 'Received message: %r\n' % data)
+        def handle_timer(self):
+            print time.strftime('%c')
 
 
-    p = Test(0)
+    p = Test(0, timer=5)
 
     os.write(1, 'Say a lot of things, then press write just "q" to stop: ')
     p.loop()
 
     os.write(1, 'Say a lot of things, then press write just "q" to stop: ')
     p.loop()
index f7497536835ae72670cc2ca3acbcf55ba5d58ba3..8ed3f1576b93b2db18fd793e5e4b2e99fc1615e9 100644 (file)
@@ -37,7 +37,7 @@ class PyminDaemon(eventloop.EventLoop):
     >>> PyminDaemon(Root(), ('', 9999)).run()
     """
 
     >>> PyminDaemon(Root(), ('', 9999)).run()
     """
 
-    def __init__(self, root, bind_addr=('', 9999)):
+    def __init__(self, root, bind_addr=('', 9999), timer=1):
         r"""Initialize the PyminDaemon object.
 
         See PyminDaemon class documentation for more info.
         r"""Initialize the PyminDaemon object.
 
         See PyminDaemon class documentation for more info.
@@ -47,7 +47,7 @@ class PyminDaemon(eventloop.EventLoop):
         sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
         sock.bind(bind_addr)
         # Create EventLoop
         sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
         sock.bind(bind_addr)
         # Create EventLoop
-        eventloop.EventLoop.__init__(self, sock)
+        eventloop.EventLoop.__init__(self, sock, timer=timer)
         # Create Dispatcher
         #TODO root.pymin = PyminHandler()
         self.dispatcher = dispatcher.Dispatcher(root)
         # Create Dispatcher
         #TODO root.pymin = PyminHandler()
         self.dispatcher = dispatcher.Dispatcher(root)
@@ -84,6 +84,10 @@ class PyminDaemon(eventloop.EventLoop):
             response += u'%d\n%s' % (len(result), result)
         self.file.sendto(response.encode('utf-8'), addr)
 
             response += u'%d\n%s' % (len(result), result)
         self.file.sendto(response.encode('utf-8'), addr)
 
+    def handle_timer(self):
+        r"handle_timer() -> None :: Call handle_timer() on handlers."
+        self.dispatcher.root.handle_timer()
+
     def run(self):
         r"run() -> None :: Run the event loop (shortcut to loop())"
         try:
     def run(self):
         r"run() -> None :: Run the event loop (shortcut to loop())"
         try: