threading_timer_decorator.py 2.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116
  1. from __future__ import print_function
  2. import sys
  3. import threading
  4. from functools import partial
  5. from time import sleep
  6. try:
  7. import thread
  8. except ImportError:
  9. import _thread as thread
  10. try: # use code that works the same in Python 2 and 3
  11. range, _print = xrange, print
  12. def print(*args, **kwargs):
  13. flush = kwargs.pop('flush', False)
  14. _print(*args, **kwargs)
  15. if flush:
  16. kwargs.get('file', sys.stdout).flush()
  17. except NameError:
  18. pass
  19. class Cancellation:
  20. def __init__(self, canceled=False):
  21. self.canceled = canceled
  22. def cdquit(fn_name, cancellation):
  23. if cancellation.canceled:
  24. return
  25. # print to stderr, unbuffered in Python 2.
  26. print('{0} took too long'.format(fn_name), file=sys.stderr)
  27. sys.stderr.flush() # Python 3 stderr is likely buffered.
  28. thread.interrupt_main() # raises KeyboardInterrupt
  29. def exit_after(s):
  30. '''
  31. use as decorator to exit process if
  32. function takes longer than s seconds
  33. '''
  34. def outer(fn):
  35. def inner(*args, **kwargs):
  36. c = Cancellation()
  37. timer = threading.Timer(s, partial(cdquit, cancellation=c), args=[fn.__name__])
  38. timer.start()
  39. try:
  40. result = fn(*args, **kwargs)
  41. finally:
  42. c.canceled = True
  43. timer.cancel()
  44. return result
  45. return inner
  46. return outer
  47. def call_method_with_timeout(method, timeout, *args, **kwargs):
  48. return exit_after(timeout)(method)(*args, **kwargs)
  49. @exit_after(1)
  50. def a():
  51. print('a')
  52. @exit_after(2)
  53. def b():
  54. print('b')
  55. sleep(1)
  56. @exit_after(3)
  57. def c():
  58. print('c')
  59. sleep(2)
  60. @exit_after(4)
  61. def d():
  62. print('d started')
  63. for i in range(10):
  64. sleep(1)
  65. print(i)
  66. @exit_after(5)
  67. def countdown(n):
  68. print('countdown started', flush=True)
  69. for i in range(n, -1, -1):
  70. print(i, end=', ', flush=True)
  71. sleep(1)
  72. print('countdown finished')
  73. def main():
  74. a()
  75. b()
  76. c()
  77. try:
  78. d()
  79. except KeyboardInterrupt as error:
  80. print('d should not have finished, printing error as expected:')
  81. print(error)
  82. countdown(3)
  83. countdown(10)
  84. print('This should not print!!!')
  85. if __name__ == '__main__':
  86. main()