2020import logging
2121import time
2222import tarfile
23+ import threading
2324import urllib
2425import urllib .request
2526import zipfile
@@ -129,7 +130,7 @@ def decode_and_print(str_to_decode):
129130 return output
130131
131132
132- def run_command (command_to_run , _cwd = None , _exit_on_fail = False , _output_file = None , _env = None ):
133+ def run_command (command_to_run , _cwd = None , _exit_on_fail = False , _output_file = None , _env = None , _timeout = None ):
133134 """ Runs the command.
134135
135136 Args:
@@ -138,6 +139,7 @@ def run_command(command_to_run, _cwd=None, _exit_on_fail=False, _output_file=Non
138139 _exit_on_fail (bool): If it should exit on failure.
139140 _output_file ():
140141 _env: environment for sub-process, passed to subprocess.Popen()
142+ _timeout: timeout in seconds, or None for no timeout
141143 Returns:
142144 (string, string, int): Returns a tuple of stdout, stderr, and command return code if _output_file= None
143145 Otherwise stdout, stderr are empty.
@@ -156,27 +158,44 @@ def run_command(command_to_run, _cwd=None, _exit_on_fail=False, _output_file=Non
156158 output_type = subprocess .STDOUT if _output_file else subprocess .PIPE
157159 with subprocess .Popen (command_to_run , env = _env , stdout = subprocess .PIPE , stderr = output_type , cwd = _cwd ) as proc :
158160
159- # For long running command, continuously print the output
160- if _output_file :
161- while True :
162- with open (_output_file , 'a' ) as of :
163- output = proc .stdout .readline ()
164- if proc .poll () is not None :
165- break
166- if output :
167- output_str = decode_and_print (output .strip ())
168- of .write (output_str + "\n " )
169- else :
170- command_stdout , command_stderr = proc .communicate ()
171- if len (command_stdout ) > 0 :
172- decode_and_print (command_stdout )
173- if len (command_stderr ) > 0 :
174- decode_and_print (command_stderr )
161+ timer = None
162+ if _timeout is not None :
163+ def try_kill ():
164+ try :
165+ print (" Timeout reached; killing process" )
166+ proc .kill ()
167+ except :
168+ pass
169+
170+ timer = threading .Timer (_timeout , try_kill )
171+ timer .start ()
172+
173+ try :
174+ # For long running command, continuously print the output
175+ if _output_file :
176+ while True :
177+ with open (_output_file , 'a' ) as of :
178+ output = proc .stdout .readline ()
179+ if proc .poll () is not None :
180+ break
181+ if output :
182+ output_str = decode_and_print (output .strip ())
183+ of .write (output_str + "\n " )
184+ else :
185+ command_stdout , command_stderr = proc .communicate ()
186+ if len (command_stdout ) > 0 :
187+ decode_and_print (command_stdout )
188+ if len (command_stderr ) > 0 :
189+ decode_and_print (command_stderr )
190+ finally :
191+ if timer :
192+ timer .cancel ()
175193
176194 return_code = proc .returncode
177195 if _exit_on_fail and return_code != 0 :
178196 print ("Command failed. Exiting." )
179197 sys .exit (1 )
198+
180199 return command_stdout , command_stderr , return_code
181200
182201
0 commit comments