1
1
#!/usr/bin/env python
2
2
# encoding: utf-8
3
3
#
4
- # Copyright © 2014 [email protected]
4
+ # Copyright (c) 2014 [email protected]
5
5
#
6
6
# MIT Licence. See http://opensource.org/licenses/MIT
7
7
#
8
8
# Created on 2014-04-06
9
9
#
10
10
11
- """
12
- Run background tasks
13
- """
11
+ """Run background tasks."""
14
12
15
13
from __future__ import print_function , unicode_literals
16
14
23
21
24
22
__all__ = ['is_running' , 'run_in_background' ]
25
23
26
- wf = Workflow ()
27
- log = wf .logger
24
+ _wf = None
25
+
26
+
27
+ def wf ():
28
+ global _wf
29
+ if _wf is None :
30
+ _wf = Workflow ()
31
+ return _wf
28
32
29
33
30
34
def _arg_cache (name ):
31
- """Return path to pickle cache file for arguments
35
+ """Return path to pickle cache file for arguments.
32
36
33
37
:param name: name of task
34
38
:type name: ``unicode``
35
39
:returns: Path to cache file
36
40
:rtype: ``unicode`` filepath
37
41
38
42
"""
39
-
40
- return wf .cachefile ('{}.argcache' .format (name ))
43
+ return wf ().cachefile ('{0}.argcache' .format (name ))
41
44
42
45
43
46
def _pid_file (name ):
44
- """Return path to PID file for ``name``
47
+ """Return path to PID file for ``name``.
45
48
46
49
:param name: name of task
47
50
:type name: ``unicode``
48
51
:returns: Path to PID file for task
49
52
:rtype: ``unicode`` filepath
50
53
51
54
"""
52
-
53
- return wf .cachefile ('{}.pid' .format (name ))
55
+ return wf ().cachefile ('{0}.pid' .format (name ))
54
56
55
57
56
58
def _process_exists (pid ):
57
- """Check if a process with PID ``pid`` exists
59
+ """Check if a process with PID ``pid`` exists.
58
60
59
61
:param pid: PID to check
60
62
:type pid: ``int``
61
63
:returns: ``True`` if process exists, else ``False``
62
64
:rtype: ``Boolean``
63
- """
64
65
66
+ """
65
67
try :
66
68
os .kill (pid , 0 )
67
69
except OSError : # not running
@@ -70,8 +72,7 @@ def _process_exists(pid):
70
72
71
73
72
74
def is_running (name ):
73
- """
74
- Test whether task is running under ``name``
75
+ """Test whether task is running under ``name``.
75
76
76
77
:param name: name of task
77
78
:type name: ``unicode``
@@ -107,32 +108,31 @@ def _background(stdin='/dev/null', stdout='/dev/null',
107
108
:type stderr: filepath
108
109
109
110
"""
111
+ def _fork_and_exit_parent (errmsg ):
112
+ try :
113
+ pid = os .fork ()
114
+ if pid > 0 :
115
+ os ._exit (0 )
116
+ except OSError as err :
117
+ wf ().logger .critical ('%s: (%d) %s' , errmsg , err .errno ,
118
+ err .strerror )
119
+ raise err
110
120
111
121
# Do first fork.
112
- try :
113
- pid = os .fork ()
114
- if pid > 0 :
115
- sys .exit (0 ) # Exit first parent.
116
- except OSError as e :
117
- log .critical ("fork #1 failed: (%d) %s\n " % (e .errno , e .strerror ))
118
- sys .exit (1 )
122
+ _fork_and_exit_parent ('fork #1 failed' )
123
+
119
124
# Decouple from parent environment.
120
- os .chdir (wf .workflowdir )
121
- os .umask (0 )
125
+ os .chdir (wf ().workflowdir )
122
126
os .setsid ()
127
+
123
128
# Do second fork.
124
- try :
125
- pid = os .fork ()
126
- if pid > 0 :
127
- sys .exit (0 ) # Exit second parent.
128
- except OSError as e :
129
- log .critical ("fork #2 failed: (%d) %s\n " % (e .errno , e .strerror ))
130
- sys .exit (1 )
129
+ _fork_and_exit_parent ('fork #2 failed' )
130
+
131
131
# Now I am a daemon!
132
132
# Redirect standard file descriptors.
133
- si = file (stdin , 'r' , 0 )
134
- so = file (stdout , 'a+' , 0 )
135
- se = file (stderr , 'a+' , 0 )
133
+ si = open (stdin , 'r' , 0 )
134
+ so = open (stdout , 'a+' , 0 )
135
+ se = open (stderr , 'a+' , 0 )
136
136
if hasattr (sys .stdin , 'fileno' ):
137
137
os .dup2 (si .fileno (), sys .stdin .fileno ())
138
138
if hasattr (sys .stdout , 'fileno' ):
@@ -142,8 +142,7 @@ def _background(stdin='/dev/null', stdout='/dev/null',
142
142
143
143
144
144
def run_in_background (name , args , ** kwargs ):
145
- """Pickle arguments to cache file, then call this script again via
146
- :func:`subprocess.call`.
145
+ r"""Cache arguments then call this script again via :func:`subprocess.call`.
147
146
148
147
:param name: name of task
149
148
:type name: ``unicode``
@@ -167,40 +166,39 @@ def run_in_background(name, args, **kwargs):
167
166
return immediately and will not run the specified command.
168
167
169
168
"""
170
-
171
169
if is_running (name ):
172
- log . info ('Task `{}` is already running' .format (name ))
170
+ wf (). logger . info ('Task `{0 }` is already running' .format (name ))
173
171
return
174
172
175
173
argcache = _arg_cache (name )
176
174
177
175
# Cache arguments
178
176
with open (argcache , 'wb' ) as file_obj :
179
177
pickle .dump ({'args' : args , 'kwargs' : kwargs }, file_obj )
180
- log . debug ('Command arguments cached to `{}`' .format (argcache ))
178
+ wf (). logger . debug ('Command arguments cached to `{0 }`' .format (argcache ))
181
179
182
180
# Call this script
183
181
cmd = ['/usr/bin/python' , __file__ , name ]
184
- log . debug ('Calling {!r} ...' .format (cmd ))
182
+ wf (). logger . debug ('Calling {0 !r} ...' .format (cmd ))
185
183
retcode = subprocess .call (cmd )
186
184
if retcode : # pragma: no cover
187
- log .error ('Failed to call task in background' )
185
+ wf (). logger .error ('Failed to call task in background' )
188
186
else :
189
- log . debug ('Executing task `{}` in background...' .format (name ))
187
+ wf (). logger . debug ('Executing task `{0 }` in background...' .format (name ))
190
188
return retcode
191
189
192
190
193
191
def main (wf ): # pragma: no cover
194
- """
192
+ """Run command in a background process.
193
+
195
194
Load cached arguments, fork into background, then call
196
- :meth:`subprocess.call` with cached arguments
195
+ :meth:`subprocess.call` with cached arguments.
197
196
198
197
"""
199
-
200
198
name = wf .args [0 ]
201
199
argcache = _arg_cache (name )
202
200
if not os .path .exists (argcache ):
203
- log . critical ('No arg cache found : {!r}' .format (argcache ))
201
+ wf . logger . critical ('No arg cache found : {0 !r}' .format (argcache ))
204
202
return 1
205
203
206
204
# Load cached arguments
@@ -221,23 +219,24 @@ def main(wf): # pragma: no cover
221
219
222
220
# Write PID to file
223
221
with open (pidfile , 'wb' ) as file_obj :
224
- file_obj .write ('{}' .format (os .getpid ()))
222
+ file_obj .write ('{0 }' .format (os .getpid ()))
225
223
226
224
# Run the command
227
225
try :
228
- log . debug ('Task `{}` running' .format (name ))
229
- log . debug ('cmd : {!r}' .format (args ))
226
+ wf . logger . debug ('Task `{0 }` running' .format (name ))
227
+ wf . logger . debug ('cmd : {0 !r}' .format (args ))
230
228
231
229
retcode = subprocess .call (args , ** kwargs )
232
230
233
231
if retcode :
234
- log .error ('Command failed with [{}] : {!r}' .format (retcode , args ))
232
+ wf .logger .error ('Command failed with [{0}] : {1!r}' .format (
233
+ retcode , args ))
235
234
236
235
finally :
237
236
if os .path .exists (pidfile ):
238
237
os .unlink (pidfile )
239
- log . debug ('Task `{}` finished' .format (name ))
238
+ wf . logger . debug ('Task `{0 }` finished' .format (name ))
240
239
241
240
242
241
if __name__ == '__main__' : # pragma: no cover
243
- wf .run (main )
242
+ wf () .run (main )
0 commit comments