Skip to content

3 ways to set timeout on function call in Python

homepage-banner

Introduction

Have you ever encountered a situation where you needed to set a timeout on a function call? Whether you’re working with JavaScript, Python, or any other programming language, there are several ways to achieve this. In this blog post, we will explore three different methods to set a timeout on a function call in Python.

Using Signal

import signal
import functools
import os
import errno
import time

class TimeoutError(Exception):
    pass

def timeout(seconds=10, error_message=os.strerror(errno.ETIME)):
    def decorator(func):
        def _handle_timeout(signum, frame):
            raise TimeoutError(error_message)

        @functools.wraps(func)
        def wrapper(*args, **kwargs):
            signal.signal(signal.SIGALRM, _handle_timeout)
            signal.alarm(seconds)
            try:
                result = func(*args, **kwargs)
            finally:
                signal.alarm(0)
            return result

        return wrapper

    return decorator

@timeout
def long_running_function1():
    time.sleep(6)

@timeout(5)
def long_running_function2():
    time.sleep(6)

@timeout(3, os.strerror(errno.ETIMEDOUT))
def long_running_function3():
    time.sleep(6)

if __name__ == '__main__':
    try:
        long_running_function3()
    except Exception as e:
        print(e)

or wrap in one class

import time
import signal

class Timeout:
    def __init__(self, seconds=1, error_message='Timeout'):
        self.seconds = seconds
        self.error_message = error_message

    def handle_timeout(self, signum, frame):
        raise TimeoutError(self.error_message)

    def __enter__(self):
        signal.signal(signal.SIGALRM, self.handle_timeout)
        signal.alarm(self.seconds)

    def __exit__(self, type, value, traceback):
        signal.alarm(0)

if __name__ == '__main__':
    try:
        with Timeout(seconds=3):
            time.sleep(5)
    except TimeoutError as e:
        print(e)

Using ProcessPoolExecutor

from concurrent.futures import TimeoutError
from time import sleep
import concurrent

max_numbers = [1000]

def run_loop(max_number):
    sleep(max_number)

def main():
    with concurrent.futures.ProcessPoolExecutor(max_workers=len(max_numbers)) as executor:
        try:
            for future in concurrent.futures.as_completed(executor.map(run_loop, max_numbers, timeout=1), timeout=1):
                print(future.result(timeout=1))
        except concurrent.futures._base.TimeoutError:
            print("This took to long...")
            stop_process_pool(executor)

def stop_process_pool(executor):
    for pid, process in executor._processes.items():
        process.terminate()
    executor.shutdown()

if __name__ == '__main__':
    main()

Using multiprocessing

import multiprocessing
import time

def long_running_function():
    time.sleep(10)

if __name__ == '__main__':
    p = multiprocessing.Process(target=long_running_function)
    p.start()
    p.join(3)
    if p.is_alive():
        print("timeout, terminated!")
        p.terminate()
        p.join()

Reference

  • https://docs.python.org/3/library/concurrent.futures.html
  • https://medium.com/@chaoren/how-to-timeout-in-python-726002bf2291
  • https://stackoverflow.com/questions/492519/timeout-on-a-function-call
  • https://alexandra-zaharia.github.io/posts/function-timeout-in-python-signal/
Leave your message