I asked this question as continuation of Limit function execution in Python
I found a way to do it without threads etc. Just simple checking from time to time.
Here is my decorator:
def time_limit(seconds):
def decorator(func):
func.info = threading.local()
def check_timeout():
if hasattr(func.info, 'end_time'):
if time.time() > func.info.end_time:
raise TimeoutException
func.check_timeout = check_timeout
@functools.wraps(func)
def wrapper(*args, **kwargs):
if not hasattr(func.info, 'end_time'):
func.info.end_time = time.time() + seconds
return func(*args, **kwargs)
return wrapper
return decorator
And usage:
@time_limit(60)
def algo():
do_something()
algo.check_timeout()
do_something_else()
It works fine on localhost, but it fails on server apache with mod_wsgi and django.
- First problem. Notice
hasattr
? I should add it, because from time to time I got error'_thread.local'
has no attributeend_time
- Why do I need threading.local? As @Graham Dumpleton pointed out, we can't have a single global end time as a subsequent request will come in and overwrite it. So if the first request hadn't finished, its
end_time
would get reset to whatever was set for the later request. The problem is that this approach doesn't help. Suppose I have following session of runs.
First run - before timeout occurs - runs perfectly
Second run - before timeout occurs - runs perfectly
Third run - timeout occurs - raises TimeoutException
All subsequent calls raise TimeoutException
no matter was it or not.
It seems like all subsequent calls look at end_time copy of third run, and since there is Timeout, they also raise Timeout
.
How can I localize end_time for each function call? Thank you.
EDIT: Thanks to @miki725 and @Antti Haapala I simplified my function and made it a simple class:
class TimeLimit(object):
def __init__(self, timeout=60):
self.timeout = timeout
self.end = None
def check_timeout(self):
if self.end and time.time() > self.end:
raise TimeoutException
else:
self.start()
def start(self):
if not self.end:
self.end = time.time() + self.timeout
However, it is very inconvenient for me to pass timer to function, because algo is actually very complex recursive function. So, I did following:
timer = TimeLimit() # fails. I think because it is global
def algo()
do_stuff()
timer.check_timeout()
do_another_stuff()
sub_algo() # check inside it to
algo()
...
Is there any way to make timer
thread-safe. Is pseudoprivate _timer
of any help?