Archive

Posts Tagged ‘SIGALRM’

Terminate a script after X seconds

December 8, 2012 Leave a comment

Problem
In Qt, there is a class called QTimer. With QTimer you can, for instance, terminate your application in X seconds. Example:

#!/usr/bin/env python

import sys

from PySide.QtCore import *
from PySide.QtGui import *

SEC = 1000    # 1 sec. is 1000 msec.

def main():
    app = QApplication(sys.argv)
    form = QDialog()
    form.show()
    # suicide in 3 seconds:
    QTimer.singleShot(3 * SEC, app.quit)
    app.exec_()

if __name__ == "__main__":
   main()

Question: how to have the same effect in a command-line application?

Solution
I came up with the following solution:

#!/usr/bin/env python

import sys
from time import sleep
import signal

class MyTimer(object):
    """
    Similar to Qt's QTimer. Call a function in a specified time.

    Time is given in sec. Usage:
    mt = MyTimer()
    mt.singleShot(<sec>, <function_name>)

    After setting it, you can still disable it:
    mt.disable()

    If you call it several times, any previously scheduled alarm
    will be canceled (only one alarm can be scheduled at any time).
    """
    def singleShot(self, sec, func):
        self.f = func
        signal.signal(signal.SIGALRM, self.handler)
        signal.alarm(sec)

    def handler(self, *args):
        self.f()

    def disable(self):
        signal.alarm(0)

def main():
    while True:
        print '.',
        sleep(0.5)
        
if __name__ == "__main__":
    mt = MyTimer()
    mt.singleShot(3, sys.exit)
    main()

As can be seen, the main function has an eternal loop. However, this program will terminate in 3 seconds. I’m imitating QTimer’s singleshot. The differences: (1) you must create a MyTimer object, and (2) time is given in seconds, not in milliseconds. You can also write it in one line if you want: MyTimer().singleShot(3, sys.exit).

It is written in a general form, so instead of sys.exit, you can also call a different function.

Raise a timeout exception after X seconds

December 8, 2012 Leave a comment

Problem
You make a call that may stuck (for instance downloading a webpage). How to timeout after some time?

Solution
I found the following tip at http://stackoverflow.com/questions/8464391 (Unix only):

import signal
import time

def test_request(arg=None):
    """Your http request."""
    time.sleep(2)
    return arg

class Timeout():
    """Timeout class using ALARM signal."""
    class Timeout(Exception):
        pass

    def __init__(self, sec):
        self.sec = sec

    def __enter__(self):
        signal.signal(signal.SIGALRM, self.raise_timeout)
        signal.alarm(self.sec)

    def __exit__(self, *args):
        signal.alarm(0)    # disable alarm

    def raise_timeout(self, *args):
        raise Timeout.Timeout()

def main():
    # Run block of code with timeouts
    try:
        with Timeout(3):
            print test_request("Request 1")
        with Timeout(1):
            print test_request("Request 2")
    except Timeout.Timeout:
        print "Timeout"

#############################################################################

if __name__ == "__main__":
    main()

Note that time for signal.alarm must be specified in seconds (integer value).

Output:

Request 1
Timeout
Categories: python Tags: , , ,