#9-2020-Mar-“Delay a function or setTimeout equivalent for python. How to do it and be aware of hanging threads”¶
Example use case: You are running a bash script and want to make sure it is closing
import threading from functools import wraps from bash_utils import bash def delay(delay=0.0): """ Decorator delaying the execution of a function for a while. """ def wrap(f): @wraps(f) def delayed(*args, **kwargs): timer = threading.Timer(delay, f, args=args, kwargs=kwargs) timer.start() return delayed return wrap script_proc = bash('docker build image') @delay(120) def give_up_build(): if not script_proc.done(): script_proc.kill() give_up_build()
The decorator works, but if you are doing multiple builds it might lead to a lot of hanging threads. A better example is when you are testing your code, then pytest will mark the test as green, but the test will still be spinning until 2 minutes have passed.
Class with cancel¶
A better approach is to cancel the timer on success
import threading from typing import Callable from bash_utils import bash class SafeDelay: def __init__(self, t: float, func: Callable, *args, **kwargs): self.t = t self.func = func self(*args, **kwargs) def __call__(self, *args, **kwargs): timer = threading.Timer(self.t, self.func, args=args, kwargs=kwargs) timer.start() self.timer = timer def cancel(self): if self.timer: self.timer.cancel() script_proc = bash('docker build image') def give_up_build(): if not script_proc.done(): script_proc.kill() give_up_build = SafeDelay(120, give_up_build) script_proc.complete_flag.add_done_callback(lambda _: give_up_build.cancel()) # CANCEL
The difference is:
The delay is not a decorator anymore and we call it immediately to initialize the self.timer
We support cancel
Notice the script_proc.complete_flag is a
from concurrent.futures import FutureIf there is interest I will show how to create the bash in another article.
Spinning up threads is easy. But MAN can it be a pain in the ass when the test continues to spin, or the process doesn’t shutdown cleanly. So be careful with the lifetime of your threads and processes.
I will end the article with a few key takeaways from an excellent presentation on multiprocessing by Pamela McA’Nulty
Don’t share pass messages
Always clean up after yourself
Handle TERM and INT signals
Don’t ever wait forever
Report and log all the things