timeout 2.1 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485
  1. #!/usr/bin/env python
  2. """
  3. Use to put a timeout on the length of time a script can run. This is
  4. especially useful for checking for scripts that hang.
  5. Usage
  6. =====
  7. NOTE: Make sure you run ``pip install -r requirements-dev.txt`` before running.
  8. To use the script, run::
  9. ./timeout "./my-script-to-run" --timeout-after 5
  10. """
  11. import argparse
  12. import os
  13. import subprocess
  14. import sys
  15. import time
  16. import psutil
  17. class TimeoutException(Exception):
  18. def __init__(self, timeout_len):
  19. msg = 'Script failed to complete within %s seconds' % timeout_len
  20. Exception.__init__(self, msg)
  21. def timeout(args):
  22. parent_pid = os.getpid()
  23. child_p = run_script(args)
  24. try:
  25. run_timeout(child_p.pid, args.timeout_after)
  26. except (TimeoutException, KeyboardInterrupt) as e:
  27. proc = psutil.Process(parent_pid)
  28. procs = proc.children(recursive=True)
  29. for child in procs:
  30. child.terminate()
  31. gone, alive = psutil.wait_procs(procs, timeout=1)
  32. for child in alive:
  33. child.kill()
  34. raise e
  35. def run_timeout(pid, timeout_len):
  36. p = psutil.Process(pid)
  37. start_time = time.time()
  38. while p.is_running():
  39. if p.status() == psutil.STATUS_ZOMBIE:
  40. p.kill()
  41. break
  42. current_time = time.time()
  43. # Raise a timeout if the duration of the process is longer than
  44. # the desired timeout.
  45. if current_time - start_time > timeout_len:
  46. raise TimeoutException(timeout_len)
  47. time.sleep(1)
  48. def run_script(args):
  49. return subprocess.Popen(args.script, shell=True)
  50. def main():
  51. parser = argparse.ArgumentParser(usage=__doc__)
  52. parser.add_argument('script', help='The script to run for benchmarking')
  53. parser.add_argument(
  54. '--timeout-after',
  55. required=True,
  56. type=float,
  57. help=(
  58. 'The length of time in seconds allowed for the script to run '
  59. 'before it time\'s out.'
  60. ),
  61. )
  62. args = parser.parse_args()
  63. return timeout(args)
  64. if __name__ == '__main__':
  65. sys.exit(main())