@@ -79,28 +79,45 @@ def _py2_str(src):
7979class Subprocess (BaseSingleton ):
8080 """Subprocess helper with timeouts and lock-free FIFO."""
8181
82- __lock = threading .RLock ()
83-
84- __slots__ = ()
82+ __slots__ = (
83+ '__lock' ,
84+ '__process' ,
85+ )
8586
8687 def __init__ (self ):
8788 """Subprocess helper with timeouts and lock-free FIFO.
8889
8990 For excluding race-conditions we allow to run 1 command simultaneously
9091 """
91- pass
92+ self .__lock = threading .RLock ()
93+ self .__process = None
94+
95+ @property
96+ def lock (self ): # type: () -> threading.RLock
97+ """Lock.
98+
99+ :rtype: threading.RLock
100+ """
101+ return self .__lock
92102
93103 def __enter__ (self ):
94104 """Context manager usage."""
105+ self .lock .acquire ()
95106 return self
96107
97108 def __exit__ (self , exc_type , exc_val , exc_tb ):
98109 """Context manager usage."""
99- pass
110+ if self .__process :
111+ self .__process .kill ()
112+ self .lock .release ()
113+
114+ def __del__ (self ):
115+ """Destructor. Kill running subprocess, if it running."""
116+ if self .__process :
117+ self .__process .kill ()
100118
101- @classmethod
102119 def __exec_command (
103- cls ,
120+ self ,
104121 command , # type: str
105122 cwd = None , # type: typing.Optional[str]
106123 env = None , # type: typing.Optional[typing.Dict[str, typing.Any]]
@@ -146,43 +163,41 @@ def poll_streams(
146163
147164 @threaded .threaded (started = True , daemon = True )
148165 def poll_pipes (
149- proc , # type: subprocess.Popen
150166 result , # type: exec_result.ExecResult
151167 stop # type: threading.Event
152168 ):
153169 """Polling task for FIFO buffers.
154170
155- :type proc: subprocess.Popen
156171 :type result: exec_result.ExecResult
157172 :type stop: threading.Event
158173 """
159174 while not stop .isSet ():
160175 time .sleep (0.1 )
161176 poll_streams (
162177 result = result ,
163- stdout = proc .stdout ,
164- stderr = proc .stderr ,
178+ stdout = self . __process .stdout ,
179+ stderr = self . __process .stderr ,
165180 )
166181
167- proc .poll ()
182+ self . __process .poll ()
168183
169- if proc .returncode is not None :
184+ if self . __process .returncode is not None :
170185 result .read_stdout (
171- src = proc .stdout ,
186+ src = self . __process .stdout ,
172187 log = logger ,
173188 verbose = verbose
174189 )
175190 result .read_stderr (
176- src = proc .stderr ,
191+ src = self . __process .stderr ,
177192 log = logger ,
178193 verbose = verbose
179194 )
180- result .exit_code = proc .returncode
195+ result .exit_code = self . __process .returncode
181196
182197 stop .set ()
183198
184199 # 1 Command per run
185- with cls . __lock :
200+ with self . lock :
186201 result = exec_result .ExecResult (cmd = command )
187202 stop_event = threading .Event ()
188203 message = _log_templates .CMD_EXEC .format (cmd = command .rstrip ())
@@ -191,18 +206,18 @@ def poll_pipes(
191206 else :
192207 logger .debug (message )
193208 # Run
194- process = subprocess .Popen (
209+ self . __process = subprocess .Popen (
195210 args = [command ],
196211 stdin = subprocess .PIPE ,
197212 stdout = subprocess .PIPE ,
198213 stderr = subprocess .PIPE ,
199214 shell = True , cwd = cwd , env = env ,
200- universal_newlines = False )
215+ universal_newlines = False ,
216+ )
201217
202218 # Poll output
203219 # pylint: disable=assignment-from-no-return
204220 poll_thread = poll_pipes (
205- process ,
206221 result ,
207222 stop_event
208223 ) # type: threading.Thread
@@ -213,17 +228,19 @@ def poll_pipes(
213228 # Process closed?
214229 if stop_event .isSet ():
215230 stop_event .clear ()
231+ self .__process = None
216232 return result
217233 # Kill not ended process and wait for close
218234 try :
219- process .kill () # kill -9
235+ self . __process .kill () # kill -9
220236 stop_event .wait (5 )
221237 poll_thread .join (5 )
222238 except OSError :
223239 # Nothing to kill
224240 logger .warning (
225241 u"{!s} has been completed just after timeout: "
226242 "please validate timeout." .format (command ))
243+ self .__process = None
227244
228245 wait_err_msg = _log_templates .CMD_WAIT_ERROR .format (
229246 cmd = command .rstrip (),
@@ -239,9 +256,8 @@ def poll_pipes(
239256 wait_err_msg + output_brief_msg
240257 )
241258
242- @classmethod
243259 def execute (
244- cls ,
260+ self ,
245261 command , # type: str
246262 verbose = False , # type: bool
247263 timeout = None , # type: typing.Optional[int]
@@ -257,8 +273,8 @@ def execute(
257273 :rtype: ExecResult
258274 :raises: ExecHelperTimeoutError
259275 """
260- result = cls .__exec_command (command = command , timeout = timeout ,
261- verbose = verbose , ** kwargs )
276+ result = self .__exec_command (command = command , timeout = timeout ,
277+ verbose = verbose , ** kwargs )
262278 message = _log_templates .CMD_RESULT .format (
263279 cmd = command , code = result .exit_code )
264280 logger .log (
@@ -268,9 +284,8 @@ def execute(
268284
269285 return result
270286
271- @classmethod
272287 def check_call (
273- cls ,
288+ self ,
274289 command , # type: str
275290 verbose = False , # type: bool
276291 timeout = None , # type: typing.Optional[int]
@@ -293,7 +308,7 @@ def check_call(
293308 :raises: DevopsCalledProcessError
294309 """
295310 expected = proc_enums .exit_codes_to_enums (expected )
296- ret = cls .execute (command , verbose , timeout , ** kwargs )
311+ ret = self .execute (command , verbose , timeout , ** kwargs )
297312 if ret ['exit_code' ] not in expected :
298313 message = (
299314 _log_templates .CMD_UNEXPECTED_EXIT_CODE .format (
@@ -312,9 +327,8 @@ def check_call(
312327 stderr = ret ['stderr_brief' ])
313328 return ret
314329
315- @classmethod
316330 def check_stderr (
317- cls ,
331+ self ,
318332 command , # type: str
319333 verbose = False , # type: bool
320334 timeout = None , # type: typing.Optional[int]
@@ -334,7 +348,7 @@ def check_stderr(
334348 :rtype: ExecResult
335349 :raises: DevopsCalledProcessError
336350 """
337- ret = cls .check_call (
351+ ret = self .check_call (
338352 command , verbose , timeout = timeout ,
339353 error_info = error_info , raise_on_err = raise_on_err , ** kwargs )
340354 if ret ['stderr' ]:
0 commit comments