-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathtimeout.py
96 lines (83 loc) · 3.02 KB
/
timeout.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
import multiprocessing
from Queue import Empty
from inspect import getargspec as signature
try:
import dill
except ImportError:
pass
__all__ = ['timeout', 'TimeoutError']
class TimeoutError(Exception):
pass
class timeout(object):
def __init__(self, using_dill=False, raise_error=False):
self.using_dill = using_dill
self.raise_error = raise_error
@staticmethod
def container(queue, payloads, using_dill=False):
try:
if using_dill:
func, args, kwargs = dill.loads(payloads)
else:
func, args, kwargs = payloads
except Exception as e:
result = e
else:
try:
result = func(*args, **kwargs)
except Exception as e:
result = e
finally:
queue.put(result)
def __call__(self, func, args=(), kwargs={}, timeout=1, default=None, raise_error=None):
multiprocessing.freeze_support()
if self.using_dill:
payloads = dill.dumps((func, args, kwargs))
else:
payloads = func, args, kwargs
queue = multiprocessing.Queue()
process = multiprocessing.Process(target = self.__class__.container, args = (queue, payloads, self.using_dill))
result = default
raise_error = raise_error if isinstance(raise_error, bool) else self.raise_error
try:
process.start()
result = queue.get(block=True, timeout=timeout)
except Empty:
process.terminate()
if hasattr(func, '__name__'):
message = 'Method:{func_name}-{func_args}'.format(func_name=func.__name__, func_args=signature(func))
else:
message = 'Method:{func_obj}'.format(func_obj=func)
result = TimeoutError('{message}, Timeout:{time}\'s'.format(message=message, time=timeout))
finally:
queue.close()
process.join()
if isinstance(result, Exception):
if raise_error:
raise result # pylint: disable=E0702
return default
return result
if __name__ == '__main__':
def foo(x=1):
import time
cnt = 1
while True:
time.sleep(1)
print(x, cnt)
cnt += 1
print(timeout()(foo, kwargs={'x': 'Hi'}, timeout=3, default='Bye'))
print(timeout(using_dill=True)(foo, args=(2,), timeout=2, default='Sayonara'))
print(timeout()(foo, args=(2,), timeout=2, raise_error=True))
"""
>>> ('Hi', 1)
>>> ('Hi', 2)
>>> Bye
>>> (2, 1)
>>> Sayonara
>>> (2, 1)
>>> Traceback (most recent call last):
File "timeout.py", line 82, in <module>
print(timeout()(foo, args=(2,), timeout=2, raise_error=True))
File "timeout.py", line 66, in __call__
raise result # pylint: disable=E0702
__main__.TimeoutError: Method:foo-ArgSpec(args=['x'], varargs=None, keywords=None, defaults=(1,)), Timeout:2's
"""