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
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
|
# R overlay -- tools, subprocess helpers
# -*- coding: utf-8 -*-
# Copyright (C) 2014 André Erdmann <dywi@mailerd.de>
# Distributed under the terms of the GNU General Public License;
# either version 2 of the License, or (at your option) any later version.
import os
import subprocess
import sys
import time
__all__ = [
'get_subproc_devnull',
'subproc_send_term', 'subproc_send_kill',
'stop_subprocess', 'gracefully_stop_subprocess',
'create_subprocess', 'run_subprocess'
]
# python >= 3.3 has ProcessLookupError, use more generic exception otherwise
if sys.hexversion >= 0x3030000:
_ProcessLookupError = ProcessLookupError
else:
_ProcessLookupError = OSError
# python >= 3.3:
if hasattr ( subprocess, 'DEVNULL' ):
def get_subproc_devnull(mode=None):
"""Returns a devnull object suitable
for passing it as stdin/stdout/stderr to subprocess.Popen().
Python 3.3 and later variant: uses subprocess.DEVNULL
arguments:
* mode -- ignored
"""
return subprocess.DEVNULL
else:
def get_subproc_devnull(mode='a+'):
"""Returns a devnull object suitable
for passing it as stdin/stdout/stderr to subprocess.Popen().
Python 3.2 and earlier variant: opens os.devnull
arguments:
* mode -- mode for open(). Defaults to read/append
"""
return open ( os.devnull, mode )
# --
def _proc_catch_lookup_err ( func ):
def wrapped ( proc ):
try:
func ( proc )
except _ProcessLookupError:
return False
return True
return wrapped
@_proc_catch_lookup_err
def subproc_send_term ( proc ):
proc.terminate()
@_proc_catch_lookup_err
def subproc_send_kill ( proc ):
proc.kill()
def stop_subprocess ( proc, kill_timeout_cs=10 ):
"""Terminates or kills a subprocess created by subprocess.Popen().
Sends SIGTERM first and sends SIGKILL if the process is still alive after
the given timeout.
Returns: None
arguments:
* proc -- subprocess
* kill_timeout_cs -- max time to wait after terminate() before sending a
kill signal (in centiseconds). Should be an int.
Defaults to 10 (= 1s).
"""
if not subproc_send_term ( proc ):
return
try:
for k in range ( kill_timeout_cs ):
if proc.poll() is not None:
return
time.sleep ( 0.1 )
except:
subproc_send_kill ( proc )
else:
subproc_send_kill ( proc )
# --- end of stop_subprocess (...) ---
def gracefully_stop_subprocess ( proc, **kill_kwargs ):
try:
if subproc_send_term ( proc ):
proc.communicate()
except:
stop_subprocess ( proc, **kill_kwargs )
raise
def create_subprocess ( cmdv, **kwargs ):
"""subprocess.Popen() wrapper that redirects stdin/stdout/stderr to
devnull or to a pipe if set to False/True.
Returns: subprocess
arguments:
* cmdv --
* **kwargs --
"""
devnull_obj = None
for key in { 'stdin', 'stdout', 'stderr' }:
if key not in kwargs:
pass
elif kwargs [key] is True:
kwargs [key] = subprocess.PIPE
elif kwargs [key] is False:
if devnull_obj is None:
devnull_obj = get_subproc_devnull()
assert devnull_obj is not None
kwargs [key] = devnull_obj
# else don't care
# --
return subprocess.Popen ( cmdv, **kwargs )
# --- end of create_subprocess (...) ---
def run_subprocess ( cmdv, kill_timeout_cs=10, **kwargs ):
"""Calls create_subprocess() and waits for the process to exit.
Catches exceptions and terminates/kills the process in that case
Returns: 2-tuple ( subprocess, output )
arguments:
* cmdv --
* kill_timeout_cs -- time to wait after SIGTERM before sending SIGKILL
(in centiseconds), see stop_subprocess() for details
Defaults to 10.
* **kwargs --
"""
proc = None
try:
proc = create_subprocess ( cmdv, **kwargs )
output = proc.communicate()
except:
if proc is not None:
stop_subprocess ( proc, kill_timeout_cs=kill_timeout_cs )
raise
return ( proc, output )
# --- end of run_subprocess (...) ---
|