Search code examples
pythoninputtimeoutsignals

How can a "dead man's switch" be written in Python, something that monitors for an interaction in a specified time?


I want to have a Python script running that looks for someone to interact with it in a specified time period (say, a week). If someone interacts with it, it continues to another loop of looking for an interaction. If someone does not interact with it, then it starts performing some actions.

I have started such a script using the module signal (and an example timeout of 20 seconds), but the timeout doesn't seem to be working; the script immediately launches into non-interaction actions. What is going wrong? Is there a better approach to this problem?

#!/usr/bin/env python

import propyte

import signal
import time

def main():

    response = "yes"
    while response == "yes":
        response = get_input_nonblocking(
            prompt          = "ohai?",
            timeout         = 20 #604800 s (1 week)
        )
    print("start non-response procedures")
    # do things

def alarm_handler(signum, frame):
    raise Exception

def get_input_nonblocking(
    prompt          = "",
    timeout         = 20, # seconds
    message_timeout = "prompt timeout"
    ):
    signal.signal(signal.SIGALRM, alarm_handler)
    signal.alarm(timeout)
    try:
        response = propyte.get_input(prompt)
        signal.alarm(0)
        return response
    except Exception:
        print(message_timeout)
    signal.signal(signal.SIGALRM, signal.SIG_IGN)
    return ""

if __name__ == '__main__':
    main()

Solution

  • You can simply write:

    import signal
    TIMEOUT = 20 * 60 # secs to wait for interaction
    
    def interrupted(signum, frame):
        "called when read times out"
        print('Exiting')
    
    signal.signal(signal.SIGALRM, interrupted)
    
    def i_input():
        try:
                print('You have 20 minutes to interact or this script will cease to execute')
                foo = input()
                return foo
        except:
                # timeout
                return
    
    # set alarm
    signal.alarm(TIMEOUT)
    
    inp = i_input()
    
    # disable the alarm if not wanted any longer
    # signal.alarm(0)