rsclib 0.65__py3-none-any.whl → 0.66__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- rsclib/Version.py +1 -1
- rsclib/__init__.py +4 -1
- rsclib/execute.py +145 -143
- rsclib/hexdump.py +13 -3
- rsclib/iter_recipes.py +16 -0
- rsclib/stateparser.py +52 -52
- rsclib-0.66.dist-info/METADATA +700 -0
- {rsclib-0.65.dist-info → rsclib-0.66.dist-info}/RECORD +11 -11
- {rsclib-0.65.dist-info → rsclib-0.66.dist-info}/WHEEL +1 -1
- rsclib-0.65.dist-info/METADATA +0 -692
- {rsclib-0.65.dist-info → rsclib-0.66.dist-info}/entry_points.txt +0 -0
- {rsclib-0.65.dist-info → rsclib-0.66.dist-info}/top_level.txt +0 -0
rsclib/execute.py
CHANGED
@@ -1,6 +1,5 @@
|
|
1
|
-
#!/usr/bin/
|
2
|
-
#
|
3
|
-
# Copyright (C) 2009-17 Dr. Ralf Schlatterbeck Open Source Consulting.
|
1
|
+
#!/usr/bin/python3
|
2
|
+
# Copyright (C) 2009-24 Dr. Ralf Schlatterbeck Open Source Consulting.
|
4
3
|
# Reichergasse 131, A-3411 Weidling.
|
5
4
|
# Web: http://www.runtux.com Email: office@runtux.com
|
6
5
|
# All rights reserved
|
@@ -33,32 +32,32 @@ from traceback import format_exc
|
|
33
32
|
from subprocess import Popen, PIPE
|
34
33
|
from rsclib.autosuper import autosuper
|
35
34
|
|
36
|
-
class Exec_Error
|
35
|
+
class Exec_Error (RuntimeError): pass
|
37
36
|
|
38
|
-
class _Named (autosuper)
|
37
|
+
class _Named (autosuper):
|
39
38
|
@property
|
40
|
-
def clsname (self)
|
39
|
+
def clsname (self):
|
41
40
|
return self.__class__.__name__.lower ().replace ('_', '-')
|
42
41
|
# end def clsname
|
43
42
|
# end class _Named
|
44
43
|
|
45
|
-
class Log (_Named)
|
44
|
+
class Log (_Named):
|
46
45
|
""" Logger mixin. Sets up a log prefix automagically but can take
|
47
46
|
arguments for setting log_level and log_prefix.
|
48
47
|
Use as self.log.debug (msg), self.log.info (msg) etc.
|
49
48
|
"""
|
50
49
|
logprefix = ''
|
51
|
-
def __init__ (self, log_level = None, log_prefix = None, *args, **kw)
|
50
|
+
def __init__ (self, log_level = None, log_prefix = None, *args, **kw):
|
52
51
|
log_prefix = log_prefix or '%s%s' % (self.logprefix, self.clsname)
|
53
52
|
self.log_prefix = log_prefix
|
54
53
|
self.log = logging.getLogger (log_prefix)
|
55
|
-
if not len (self.log.handlers)
|
54
|
+
if not len (self.log.handlers):
|
56
55
|
log_level = log_level or logging.DEBUG
|
57
56
|
formatter = logging.Formatter \
|
58
57
|
('%s[%%(process)d]: %%(message)s' % log_prefix)
|
59
|
-
if 'log_handler' in args
|
58
|
+
if 'log_handler' in args:
|
60
59
|
handler = args ['log_handler']
|
61
|
-
else
|
60
|
+
else:
|
62
61
|
handler = SysLogHandler ('/dev/log', 'daemon')
|
63
62
|
handler.setLevel (log_level)
|
64
63
|
handler.setFormatter (formatter)
|
@@ -81,21 +80,21 @@ class Log (_Named) :
|
|
81
80
|
self.stderr_handler = handler
|
82
81
|
# end def add_stderr_handler
|
83
82
|
|
84
|
-
def log_exception (self)
|
85
|
-
for l in format_exc ().split ('\n')
|
86
|
-
if l
|
83
|
+
def log_exception (self):
|
84
|
+
for l in format_exc ().split ('\n'):
|
85
|
+
if l:
|
87
86
|
self.log.error (l)
|
88
87
|
# end def log_exception
|
89
88
|
|
90
|
-
def print_exception (self)
|
91
|
-
for l in format_exc ().split ('\n')
|
92
|
-
if l
|
89
|
+
def print_exception (self):
|
90
|
+
for l in format_exc ().split ('\n'):
|
91
|
+
if l:
|
93
92
|
print ("%s\r" % l)
|
94
93
|
# end def print_exception
|
95
94
|
# end class Log
|
96
95
|
|
97
|
-
class Exec (Log)
|
98
|
-
def error (self, msg)
|
96
|
+
class Exec (Log):
|
97
|
+
def error (self, msg):
|
99
98
|
""" Just print error here, a derived class may want to do
|
100
99
|
cleanup actions.
|
101
100
|
"""
|
@@ -108,9 +107,10 @@ class Exec (Log) :
|
|
108
107
|
, ignore_err = False
|
109
108
|
, shell = False
|
110
109
|
, charset = 'utf-8'
|
111
|
-
|
110
|
+
, do_split = True
|
111
|
+
):
|
112
112
|
popen_stdin = None
|
113
|
-
if stdin is not None
|
113
|
+
if stdin is not None:
|
114
114
|
popen_stdin = PIPE
|
115
115
|
p = Popen \
|
116
116
|
( args
|
@@ -121,48 +121,50 @@ class Exec (Log) :
|
|
121
121
|
)
|
122
122
|
stdout, stderr = (x.decode (charset) for x in p.communicate (stdin))
|
123
123
|
self.stderr = err = ' '.join (stderr.rstrip ().split ('\n'))
|
124
|
-
if p.returncode != 0
|
124
|
+
if p.returncode != 0:
|
125
125
|
arg = args [0]
|
126
|
-
if shell
|
126
|
+
if shell:
|
127
127
|
arg = args
|
128
128
|
msg = "Nonzero exitcode %s from %s: %s" % (p.returncode, arg, err)
|
129
129
|
self.error (msg)
|
130
|
-
for e in stderr.rstrip ().split ('\n')
|
130
|
+
for e in stderr.rstrip ().split ('\n'):
|
131
131
|
self.error (e)
|
132
|
-
if not ignore_err
|
132
|
+
if not ignore_err:
|
133
133
|
raise Exec_Error (msg, p.returncode)
|
134
|
-
|
134
|
+
if do_split:
|
135
|
+
return stdout.rstrip ().split ('\n')
|
136
|
+
return stdout
|
135
137
|
# end def exec_pipe
|
136
138
|
# end class Exec
|
137
139
|
|
138
|
-
class Lock_Mixin (_Named)
|
139
|
-
def __init__ (self, do_lock = True, *args, **kw)
|
140
|
+
class Lock_Mixin (_Named):
|
141
|
+
def __init__ (self, do_lock = True, *args, **kw):
|
140
142
|
self.need_unlock = False
|
141
143
|
self.__super.__init__ (*args, **kw)
|
142
144
|
lockdir = os.environ.get ('LOCKDIR', '/var/lock')
|
143
|
-
if getattr (self, 'lockfile', None)
|
145
|
+
if getattr (self, 'lockfile', None):
|
144
146
|
lf = self.lockfile
|
145
|
-
if not lf.startswith ('/')
|
147
|
+
if not lf.startswith ('/'):
|
146
148
|
lf = os.path.join (lockdir, lf)
|
147
|
-
if not lf.endswith ('.lock')
|
149
|
+
if not lf.endswith ('.lock'):
|
148
150
|
lf += '.lock'
|
149
151
|
self.lockfile = lf
|
150
|
-
else
|
152
|
+
else:
|
151
153
|
self.lockfile = os.path.join (lockdir, '%s.lock' % self.clsname)
|
152
|
-
if do_lock
|
154
|
+
if do_lock:
|
153
155
|
self.lock ()
|
154
156
|
# end def __init__
|
155
157
|
|
156
|
-
def lock (self)
|
158
|
+
def lock (self):
|
157
159
|
atexit.register (self.unlock)
|
158
160
|
self.need_unlock = True
|
159
|
-
try
|
161
|
+
try:
|
160
162
|
fd = os.open (self.lockfile, os.O_CREAT | os.O_EXCL | os.O_RDWR)
|
161
163
|
f = os.fdopen (fd, 'w')
|
162
164
|
f.write ("%s\n" % os.getpid())
|
163
165
|
f.close ()
|
164
166
|
except OSError as e:
|
165
|
-
if e.errno != errno.EEXIST
|
167
|
+
if e.errno != errno.EEXIST:
|
166
168
|
self.log_exception ()
|
167
169
|
raise
|
168
170
|
self.need_unlock = False
|
@@ -170,57 +172,57 @@ class Lock_Mixin (_Named) :
|
|
170
172
|
self.lock_fail ()
|
171
173
|
# end def lock
|
172
174
|
|
173
|
-
def lock_fail (self)
|
175
|
+
def lock_fail (self):
|
174
176
|
""" By default we exit when locking fails. A derived class may
|
175
177
|
want to override this.
|
176
178
|
"""
|
177
179
|
sys.exit (1)
|
178
180
|
# end def lock_fail
|
179
181
|
|
180
|
-
def unlock (self)
|
181
|
-
if self.need_unlock
|
182
|
+
def unlock (self):
|
183
|
+
if self.need_unlock:
|
182
184
|
os.unlink (self.lockfile)
|
183
185
|
self.need_unlock = False # prevent second attempt at removal
|
184
186
|
# end def unlock
|
185
187
|
__del__ = unlock
|
186
188
|
|
187
|
-
def __exit__ (self, tp, value, tb)
|
188
|
-
if tb
|
189
|
+
def __exit__ (self, tp, value, tb):
|
190
|
+
if tb:
|
189
191
|
self.log_exception ()
|
190
192
|
# We do *not* return True, we didn't handle the exception
|
191
193
|
# end def __exit__
|
192
194
|
|
193
|
-
def __enter__ (self)
|
195
|
+
def __enter__ (self):
|
194
196
|
assert not self.need_unlock
|
195
197
|
self.lock ()
|
196
198
|
# end def __enter__
|
197
199
|
|
198
200
|
# end class Lock_Mixin
|
199
201
|
|
200
|
-
class Lock (Log, Lock_Mixin)
|
202
|
+
class Lock (Log, Lock_Mixin):
|
201
203
|
|
202
|
-
def __init__ (self, lockfile = None, ** kw)
|
204
|
+
def __init__ (self, lockfile = None, ** kw):
|
203
205
|
self.lockfile = lockfile
|
204
206
|
prefix = None
|
205
|
-
if lockfile
|
207
|
+
if lockfile:
|
206
208
|
prefix = lockfile.replace ('/', '-')
|
207
209
|
self.__super.__init__ (do_lock = False, log_prefix = prefix, ** kw)
|
208
210
|
# end def __init__
|
209
211
|
|
210
212
|
# end class Lock
|
211
213
|
|
212
|
-
def exitstatus (cmd, status)
|
213
|
-
if not status
|
214
|
+
def exitstatus (cmd, status):
|
215
|
+
if not status:
|
214
216
|
return ''
|
215
217
|
sig = ''
|
216
|
-
if os.WIFSIGNALED (status)
|
218
|
+
if os.WIFSIGNALED (status):
|
217
219
|
sig = " (caught signal %d)" % os.WTERMSIG (status)
|
218
220
|
s = os.WEXITSTATUS (status)
|
219
|
-
if not s
|
221
|
+
if not s: s = ''
|
220
222
|
return 'WARNING: %s returned nonzero exit status %s%s' % (cmd, s, sig)
|
221
223
|
# end def exitstatus
|
222
224
|
|
223
|
-
class Process (Log)
|
225
|
+
class Process (Log):
|
224
226
|
""" A process in a (multi-)pipe
|
225
227
|
Note that the file descriptors can take several different
|
226
228
|
values:
|
@@ -246,7 +248,7 @@ class Process (Log) :
|
|
246
248
|
, close_stdout = False
|
247
249
|
, close_stderr = False
|
248
250
|
, **kw
|
249
|
-
)
|
251
|
+
):
|
250
252
|
self.__super.__init__ (**kw)
|
251
253
|
self.stdin = stdin
|
252
254
|
self.stdout = stdout
|
@@ -272,20 +274,20 @@ class Process (Log) :
|
|
272
274
|
# after forking off the child with the PIPE end.
|
273
275
|
self.toclose = []
|
274
276
|
self.sibling_files = []
|
275
|
-
if not self.name
|
277
|
+
if not self.name:
|
276
278
|
self.name = self.clsname
|
277
279
|
self.__super.__init__ (log_prefix = self.log_prefix, **kw)
|
278
280
|
# end def __init__
|
279
281
|
|
280
|
-
def fork (self)
|
282
|
+
def fork (self):
|
281
283
|
rclose = []
|
282
|
-
if self.stdin == 'PIPE'
|
284
|
+
if self.stdin == 'PIPE':
|
283
285
|
pipe = os.pipe ()
|
284
286
|
self.stdin = os.fdopen (pipe [0], 'r')
|
285
287
|
self.stdin_w = os.fdopen (pipe [1], 'w')
|
286
288
|
self.toclose.append (self.stdin_w)
|
287
289
|
self.close_stdin = True
|
288
|
-
if self.stdout == 'PIPE'
|
290
|
+
if self.stdout == 'PIPE':
|
289
291
|
pipe = os.pipe ()
|
290
292
|
self.stdout = os.fdopen (pipe [1], 'w')
|
291
293
|
# to close after fork:
|
@@ -293,40 +295,40 @@ class Process (Log) :
|
|
293
295
|
self.stdout_r = os.fdopen (pipe [0], 'r')
|
294
296
|
rclose.append (self.stdout_r)
|
295
297
|
self.close_stdout = True
|
296
|
-
elif self.close_stdout and self.stdout
|
298
|
+
elif self.close_stdout and self.stdout:
|
297
299
|
self.stdouts [self.stdout] = 0
|
298
|
-
if self.stderr == 'PIPE'
|
300
|
+
if self.stderr == 'PIPE':
|
299
301
|
pipe = os.pipe ()
|
300
302
|
self.stderr = os.fdopen (pipe [1], 'w')
|
301
303
|
self.stderr_r = os.fdopen (pipe [0], 'r')
|
302
304
|
rclose.append (self.stderr_r)
|
303
305
|
self.close_stderr = True
|
304
306
|
pid = os.fork ()
|
305
|
-
if pid
|
307
|
+
if pid: # parent
|
306
308
|
self.pid = pid
|
307
309
|
self.log.debug ("fork: pid: %s" % self.pid)
|
308
310
|
self.by_pid [pid] = self
|
309
|
-
if self.stderr and self.close_stderr
|
311
|
+
if self.stderr and self.close_stderr:
|
310
312
|
self.log.debug \
|
311
313
|
("fork: closing stderr: %s" % self.stderr.fileno ())
|
312
314
|
self.stderr.close ()
|
313
|
-
if self.stdin and self.close_stdin
|
315
|
+
if self.stdin and self.close_stdin:
|
314
316
|
self.log.debug \
|
315
317
|
("fork: closing stdin: %s" % self.stdin.fileno ())
|
316
318
|
self.stdin.close ()
|
317
|
-
if hasattr (self, 'stdouts')
|
318
|
-
for f in self.stdouts
|
319
|
+
if hasattr (self, 'stdouts'):
|
320
|
+
for f in self.stdouts:
|
319
321
|
self.log.debug ("fork: aux closing: %s" % f.fileno ())
|
320
322
|
f.close ()
|
321
|
-
else
|
322
|
-
for f in self.sibling_files
|
323
|
+
else: # child
|
324
|
+
for f in self.sibling_files:
|
323
325
|
self.log.debug ("fork: child closing sibling: %s" % f.fileno ())
|
324
326
|
f.close ()
|
325
|
-
for f in self.toclose
|
327
|
+
for f in self.toclose:
|
326
328
|
self.log.debug ("fork: child closing toclose: %s" % f.fileno ())
|
327
329
|
f.close ()
|
328
330
|
# close children read descriptors in parent
|
329
|
-
for c in self.children
|
331
|
+
for c in self.children:
|
330
332
|
self.log.debug \
|
331
333
|
( "closing stdin of %s->%s: %s"
|
332
334
|
% (self.name, c.name, c.stdin.fileno ())
|
@@ -341,46 +343,46 @@ class Process (Log) :
|
|
341
343
|
return rclose
|
342
344
|
# end def fork
|
343
345
|
|
344
|
-
def method (self)
|
346
|
+
def method (self):
|
345
347
|
raise NotImplementedError
|
346
348
|
# end def method
|
347
349
|
|
348
|
-
def redirect (self)
|
350
|
+
def redirect (self):
|
349
351
|
self.log.debug ("In redirect")
|
350
|
-
if self.stdin
|
352
|
+
if self.stdin:
|
351
353
|
self.log.debug \
|
352
354
|
( "stdin dup2: %s->%s"
|
353
355
|
% (self.stdin.fileno (), sys.stdin.fileno ())
|
354
356
|
)
|
355
357
|
os.dup2 (self.stdin.fileno (), sys.stdin.fileno ())
|
356
|
-
if self.stdout
|
358
|
+
if self.stdout:
|
357
359
|
self.log.debug \
|
358
360
|
( "stdout dup2: %s->%s"
|
359
361
|
% (self.stdout.fileno (), sys.stdout.fileno ())
|
360
362
|
)
|
361
363
|
os.dup2 (self.stdout.fileno (), sys.stdout.fileno ())
|
362
|
-
if self.stderr
|
364
|
+
if self.stderr:
|
363
365
|
self.log.debug \
|
364
366
|
( "stderr dup2: %s->%s"
|
365
367
|
% (self.stderr.fileno (), sys.stderr.fileno ()))
|
366
368
|
os.dup2 (self.stderr.fileno (), sys.stderr.fileno ())
|
367
|
-
if self.stdin
|
369
|
+
if self.stdin:
|
368
370
|
self.stdin.close ()
|
369
|
-
elif self.close_stdin
|
371
|
+
elif self.close_stdin:
|
370
372
|
sys.stdin.close ()
|
371
|
-
if self.stdout
|
373
|
+
if self.stdout:
|
372
374
|
self.stdout.close ()
|
373
|
-
elif self.close_stdout
|
375
|
+
elif self.close_stdout:
|
374
376
|
sys.stdout.close ()
|
375
|
-
if self.stderr
|
377
|
+
if self.stderr:
|
376
378
|
self.stderr.close ()
|
377
|
-
elif self.close_stderr
|
379
|
+
elif self.close_stderr:
|
378
380
|
sys.stderr.close ()
|
379
381
|
# end def redirect
|
380
382
|
|
381
|
-
def run (self)
|
383
|
+
def run (self):
|
382
384
|
sibling_files = []
|
383
|
-
for c in reversed (self.children)
|
385
|
+
for c in reversed (self.children):
|
384
386
|
pipe = os.pipe ()
|
385
387
|
self.stdouts [os.fdopen (pipe [1], 'w')] = c
|
386
388
|
c.stdin = os.fdopen (pipe [0], 'r')
|
@@ -393,9 +395,9 @@ class Process (Log) :
|
|
393
395
|
c.sibling_files.extend (self.sibling_files)
|
394
396
|
sibling_files.append (c.stdin)
|
395
397
|
# For dup2 in redirect to do its job:
|
396
|
-
if len (self.children) == 1
|
397
|
-
self.stdout = self.stdouts
|
398
|
-
if self.stderr_child
|
398
|
+
if len (self.children) == 1:
|
399
|
+
self.stdout = list (self.stdouts) [0]
|
400
|
+
if self.stderr_child:
|
399
401
|
pipe = os.pipe ()
|
400
402
|
self.stderr = os.fdopen (pipe [1], 'w')
|
401
403
|
self.close_stderr = True
|
@@ -405,7 +407,7 @@ class Process (Log) :
|
|
405
407
|
self.stderr_child.sibling_files.extend (self.sibling_files)
|
406
408
|
self.children.insert (0, self.stderr_child)
|
407
409
|
toclose = self.fork ()
|
408
|
-
for child in self.children
|
410
|
+
for child in self.children:
|
409
411
|
self.log.debug \
|
410
412
|
( "passing toclose to %s: %s"
|
411
413
|
% (child.name, [x.fileno () for x in self.toclose + toclose])
|
@@ -418,12 +420,12 @@ class Process (Log) :
|
|
418
420
|
# end def run
|
419
421
|
|
420
422
|
@classmethod
|
421
|
-
def wait (cls)
|
422
|
-
while cls.by_pid
|
423
|
+
def wait (cls):
|
424
|
+
while cls.by_pid:
|
423
425
|
pid, status = os.wait ()
|
424
|
-
if pid > 0
|
426
|
+
if pid > 0:
|
425
427
|
process = cls.by_pid [pid]
|
426
|
-
if status
|
428
|
+
if status:
|
427
429
|
process.log.info \
|
428
430
|
( "pid: %s: %s"
|
429
431
|
% (pid, exitstatus (process.name, status))
|
@@ -432,53 +434,53 @@ class Process (Log) :
|
|
432
434
|
del cls.by_pid [pid]
|
433
435
|
# end def wait
|
434
436
|
|
435
|
-
def _add_buffer_process (self, found = False)
|
436
|
-
if self.stderr == 'PIPE'
|
437
|
+
def _add_buffer_process (self, found = False):
|
438
|
+
if self.stderr == 'PIPE':
|
437
439
|
self.log.debug ("found stderr PIPE: %s", self.name)
|
438
|
-
if found
|
440
|
+
if found:
|
439
441
|
self.stderr = None
|
440
442
|
self.set_stderr_process (Buffer_Process (stderr = 'PIPE'))
|
441
443
|
found = True
|
442
|
-
if self.stdout == 'PIPE'
|
444
|
+
if self.stdout == 'PIPE':
|
443
445
|
self.log.debug ("found stdout PIPE: %s", self.name)
|
444
|
-
if found
|
446
|
+
if found:
|
445
447
|
self.stdout = None
|
446
448
|
self.append (Buffer_Process (stdout = 'PIPE'))
|
447
449
|
found = True
|
448
|
-
else
|
449
|
-
for c in self.children
|
450
|
+
else:
|
451
|
+
for c in self.children:
|
450
452
|
found = c._add_buffer_process (found)
|
451
453
|
return found
|
452
454
|
# end def _add_buffer_process
|
453
455
|
|
454
|
-
def _read_outputs (self)
|
456
|
+
def _read_outputs (self):
|
455
457
|
stdout = ''
|
456
458
|
stderr = ''
|
457
|
-
if self.stderr_r
|
459
|
+
if self.stderr_r:
|
458
460
|
self.log.debug ("reading stderr: %s", self.name)
|
459
461
|
stderr = self.stderr_r.read ()
|
460
|
-
if self.stdout_r
|
462
|
+
if self.stdout_r:
|
461
463
|
self.log.debug ("reading stdout: %s", self.name)
|
462
464
|
stdout = self.stdout_r.read ()
|
463
465
|
sout_l = [stdout]
|
464
466
|
serr_l = [stderr]
|
465
|
-
for c in self.children
|
467
|
+
for c in self.children:
|
466
468
|
sout, serr = c._read_outputs ()
|
467
469
|
sout_l.append (sout)
|
468
470
|
serr_l.append (serr)
|
469
471
|
return ''.join (sout_l), ''.join (serr_l)
|
470
472
|
# end def _read_outputs
|
471
473
|
|
472
|
-
def __repr__ (self)
|
474
|
+
def __repr__ (self):
|
473
475
|
r = []
|
474
476
|
r.append ('%s:' % self.name)
|
475
|
-
if self.stdin
|
477
|
+
if self.stdin:
|
476
478
|
r.append (' stdin set')
|
477
|
-
if self.stdout
|
479
|
+
if self.stdout:
|
478
480
|
r.append (' stdout set')
|
479
|
-
if self.stderr
|
481
|
+
if self.stderr:
|
480
482
|
r.append (' stderr set')
|
481
|
-
if self.bufsize
|
483
|
+
if self.bufsize:
|
482
484
|
r.append (' bufsize = %s' % self.bufsize)
|
483
485
|
return '\n'.join (r)
|
484
486
|
# end def __repr__
|
@@ -486,80 +488,80 @@ class Process (Log) :
|
|
486
488
|
|
487
489
|
# end class Process
|
488
490
|
|
489
|
-
class Tee (Process)
|
491
|
+
class Tee (Process):
|
490
492
|
""" A tee in a pipe (like the unix command "tee" but copies to several
|
491
493
|
sub-processes)
|
492
494
|
"""
|
493
|
-
def __init__ (self, children, **kw)
|
495
|
+
def __init__ (self, children, **kw):
|
494
496
|
self.stdouts = {}
|
495
497
|
self.__super.__init__ (**kw)
|
496
498
|
self.children = children
|
497
499
|
# end def __init__
|
498
500
|
|
499
|
-
def method (self)
|
500
|
-
while 1
|
501
|
+
def method (self):
|
502
|
+
while 1:
|
501
503
|
buf = sys.stdin.read (self.bufsize)
|
502
|
-
if not buf
|
504
|
+
if not buf:
|
503
505
|
self.log.debug ("Tee: empty read, terminating")
|
504
506
|
return
|
505
507
|
# use items () here, we want to modify dict
|
506
508
|
written = False
|
507
|
-
for stdout, child in self.stdouts.items ()
|
508
|
-
if not child
|
509
|
+
for stdout, child in self.stdouts.items ():
|
510
|
+
if not child:
|
509
511
|
continue
|
510
|
-
try
|
512
|
+
try:
|
511
513
|
stdout.write (buf)
|
512
514
|
written = True
|
513
515
|
#self.log.debug ("written: %s" % len (buf))
|
514
|
-
except IOError as cause
|
516
|
+
except IOError as cause:
|
515
517
|
# this client died, no longer try to send to it
|
516
|
-
if cause.errno != errno.EPIPE
|
518
|
+
if cause.errno != errno.EPIPE:
|
517
519
|
raise
|
518
520
|
self.log.debug ("%s: dead" % child.name)
|
519
521
|
stdout.close ()
|
520
522
|
del self.stdouts [stdout]
|
521
523
|
# still clients existing?
|
522
|
-
if not written
|
523
|
-
if len (self.stdouts)
|
524
|
+
if not written:
|
525
|
+
if len (self.stdouts):
|
524
526
|
self.log.err ("All children had sigpipe ?!?")
|
525
527
|
return
|
526
528
|
# end def method
|
527
529
|
|
528
530
|
# end class Tee
|
529
531
|
|
530
|
-
class Method_Process (Process)
|
532
|
+
class Method_Process (Process):
|
531
533
|
""" A process in a (multi-)pipe which runs a given method
|
532
534
|
"""
|
533
|
-
def __init__ (self, name = None, **kw)
|
535
|
+
def __init__ (self, name = None, **kw):
|
534
536
|
self.tee = None
|
535
|
-
if 'method' in kw
|
537
|
+
if 'method' in kw:
|
536
538
|
self.method = kw ['method']
|
537
539
|
self.name = name
|
538
|
-
if not self.name
|
540
|
+
if not self.name:
|
539
541
|
self.name = self.method.__name__
|
540
542
|
self.__super.__init__ (name = self.name, **kw)
|
541
543
|
# end def __init__
|
542
544
|
|
543
|
-
def append (self, child)
|
545
|
+
def append (self, child):
|
544
546
|
assert self.stdout is None
|
545
547
|
assert child.stdin is None
|
546
|
-
if self.children
|
548
|
+
if self.children:
|
547
549
|
assert len (self.children) == 1
|
548
|
-
if not self.tee
|
550
|
+
if not self.tee:
|
549
551
|
self.tee = Tee \
|
550
552
|
(self.children, bufsize = self.bufsize)
|
551
553
|
self.children = [self.tee]
|
552
554
|
self.tee.children.append (child)
|
553
|
-
else
|
555
|
+
else:
|
554
556
|
self.children.append (child)
|
555
557
|
# end def append
|
556
558
|
|
557
|
-
def communicate (self, input = None)
|
559
|
+
def communicate (self, input = None):
|
558
560
|
assert input is None or self.stdin == 'PIPE'
|
559
561
|
p = self
|
560
|
-
if self.stdin == 'PIPE'
|
562
|
+
if self.stdin == 'PIPE':
|
561
563
|
self.stdin = None
|
562
|
-
if self.input is not None
|
564
|
+
if self.input is not None:
|
563
565
|
p = Echo_Process (message = str (input))
|
564
566
|
p.append (self)
|
565
567
|
# Add a Buffer_Process for each PIPE output in tree order.
|
@@ -572,7 +574,7 @@ class Method_Process (Process) :
|
|
572
574
|
return stdout, stderr
|
573
575
|
# end def communicate
|
574
576
|
|
575
|
-
def set_stderr_process (self, child)
|
577
|
+
def set_stderr_process (self, child):
|
576
578
|
assert self.stderr is None
|
577
579
|
assert self.stderr_child is None
|
578
580
|
assert child.stdin is None
|
@@ -581,22 +583,22 @@ class Method_Process (Process) :
|
|
581
583
|
|
582
584
|
# end class Method_Process
|
583
585
|
|
584
|
-
class Buffer_Process (Method_Process)
|
586
|
+
class Buffer_Process (Method_Process):
|
585
587
|
""" First read *everything* from stdin, then write this to stdout.
|
586
588
|
So everything is buffered in-memory. Used to decouple reading
|
587
589
|
from multiple streams.
|
588
590
|
"""
|
589
591
|
|
590
|
-
def __init__ (self, ** kw)
|
592
|
+
def __init__ (self, ** kw):
|
591
593
|
self.__super.__init__ (method = self.copy, ** kw)
|
592
|
-
if self.stdout == 'PIPE'
|
594
|
+
if self.stdout == 'PIPE':
|
593
595
|
self.io = sys.stdout
|
594
|
-
else
|
596
|
+
else:
|
595
597
|
assert self.stderr == 'PIPE'
|
596
598
|
self.io = sys.stderr
|
597
599
|
# end def __init__
|
598
600
|
|
599
|
-
def copy (self)
|
601
|
+
def copy (self):
|
600
602
|
buf = sys.stdin.read ()
|
601
603
|
self.log.debug ("read: %s" % len (buf))
|
602
604
|
self.log.debug ("write: fd=%s" % self.io.fileno ())
|
@@ -606,12 +608,12 @@ class Buffer_Process (Method_Process) :
|
|
606
608
|
|
607
609
|
# end class Buffer_Process
|
608
610
|
|
609
|
-
class Echo_Process (Method_Process)
|
611
|
+
class Echo_Process (Method_Process):
|
610
612
|
""" Simply echo a message given in constructor to stdout.
|
611
613
|
Useful for specifying a constant input to a pipeline.
|
612
614
|
"""
|
613
615
|
|
614
|
-
def __init__ (self, message, ** kw)
|
616
|
+
def __init__ (self, message, ** kw):
|
615
617
|
self.message = message
|
616
618
|
self.__super.__init__ \
|
617
619
|
( method = self.echo
|
@@ -620,40 +622,40 @@ class Echo_Process (Method_Process) :
|
|
620
622
|
)
|
621
623
|
# end def __init__
|
622
624
|
|
623
|
-
def echo (self)
|
625
|
+
def echo (self):
|
624
626
|
sys.stdout.write (self.message)
|
625
627
|
# end def echo
|
626
628
|
|
627
629
|
# end class Echo_Process
|
628
630
|
|
629
|
-
class Exec_Process (Method_Process)
|
631
|
+
class Exec_Process (Method_Process):
|
630
632
|
""" Process that execs a subcommand """
|
631
|
-
def __init__ (self, cmd, args = None, name = None, **kw)
|
633
|
+
def __init__ (self, cmd, args = None, name = None, **kw):
|
632
634
|
self.cmd = cmd
|
633
635
|
self.args = args
|
634
|
-
if not args
|
636
|
+
if not args:
|
635
637
|
self.args = [cmd]
|
636
|
-
if not name
|
638
|
+
if not name:
|
637
639
|
name = self.cmd
|
638
640
|
self.__super.__init__ (name = name, **kw)
|
639
641
|
# end def __init__
|
640
642
|
|
641
|
-
def method (self)
|
643
|
+
def method (self):
|
642
644
|
os.execv (self.cmd, self.args)
|
643
645
|
# end def method
|
644
646
|
|
645
|
-
def __repr__ (self)
|
647
|
+
def __repr__ (self):
|
646
648
|
r = [self.__super.__repr__ ()]
|
647
649
|
r.append (' cmd = %s' % self.cmd)
|
648
650
|
r.append (' args =')
|
649
|
-
for a in self.args
|
651
|
+
for a in self.args:
|
650
652
|
r.append (' %s' % a)
|
651
653
|
return '\n'.join (r)
|
652
654
|
# end def __repr__
|
653
655
|
|
654
656
|
# end class Exec_Process
|
655
657
|
|
656
|
-
def run_process (cmd, ** kw)
|
658
|
+
def run_process (cmd, ** kw):
|
657
659
|
""" Convenience function that sets up a single Exec_Process, calls
|
658
660
|
it, and returns a suitable error message if any.
|
659
661
|
"""
|