@@ -51,7 +51,8 @@ class Command:
51
51
52
52
def __init__ (self , cmd , args_subst = None , args_append = None , logger = None ,
53
53
excl_subst = False , work_dir = None , env_vars = None , timeout = None ,
54
- redirect_stderr = True , resource_limits = None , doprint = False ):
54
+ redirect_stderr = True , resource_limits = None , doprint = False ,
55
+ max_line_length = 250 , max_lines = 10000 ):
55
56
56
57
if doprint is None :
57
58
doprint = False
@@ -71,6 +72,8 @@ def __init__(self, cmd, args_subst=None, args_append=None, logger=None,
71
72
self .doprint = doprint
72
73
self .err = None
73
74
self .returncode = None
75
+ self .max_line_length = int (max_line_length )
76
+ self .max_lines = int (max_lines )
74
77
75
78
self .logger = logger or logging .getLogger (__name__ )
76
79
@@ -159,14 +162,18 @@ class OutputThread(threading.Thread):
159
162
stdout/stderr buffers fill up.
160
163
"""
161
164
162
- def __init__ (self , event , logger , doprint = False ):
165
+ def __init__ (self , event , logger , doprint = False , max_line_length = 250 , max_lines = 10000 ):
163
166
super (OutputThread , self ).__init__ ()
164
167
self .read_fd , self .write_fd = os .pipe ()
165
168
self .pipe_fobj = os .fdopen (self .read_fd , encoding = 'utf8' )
166
169
self .out = []
167
170
self .event = event
168
171
self .logger = logger
169
172
self .doprint = doprint
173
+ # Convert the maximums to integers to avoid exceptions when using them as indexes
174
+ # in case they are passed as floats.
175
+ self .max_line_length = int (max_line_length )
176
+ self .max_lines = int (max_lines )
170
177
171
178
# Start the thread now.
172
179
self .start ()
@@ -185,17 +192,31 @@ def run(self):
185
192
self .event .set ()
186
193
return
187
194
195
+ line = line .rstrip () # This will remove not only newline but also whitespace.
196
+
197
+ # Assuming that self.max_line_length is bigger than 3.
198
+ if len (line ) > self .max_line_length :
199
+ line = line [:self .max_line_length ] + "..."
200
+
201
+ # Shorten the list to be one less than the maximum because a line is going to be added.
202
+ if len (self .out ) >= self .max_lines :
203
+ self .out = self .out [- self .max_lines + 1 :]
204
+ self .out [0 ] = "... <truncated>"
205
+
188
206
self .out .append (line )
189
207
190
208
if self .doprint :
191
209
# Even if logging below fails, the thread has to keep
192
210
# running to avoid hangups of the executed command.
193
211
try :
194
- self .logger .info (line . rstrip () )
212
+ self .logger .info (line )
195
213
except Exception as print_exc :
196
214
self .logger .error (print_exc )
197
215
198
216
def getoutput (self ):
217
+ """
218
+ :return: list of lines with trailing whitespace (including newlines) stripped
219
+ """
199
220
return self .out
200
221
201
222
def fileno (self ):
@@ -226,18 +247,21 @@ def close(self):
226
247
timeout_thread = None
227
248
output_event = threading .Event ()
228
249
output_thread = OutputThread (output_event , self .logger ,
229
- doprint = self .doprint )
250
+ doprint = self .doprint ,
251
+ max_lines = self .max_lines ,
252
+ max_line_length = self .max_line_length )
230
253
231
- # If stderr redirection is off, setup a thread that will capture
232
- # stderr data.
254
+ # If stderr redirection is off, set up a thread that will capture stderr data.
233
255
stderr_thread = None
234
256
stderr_event = None
235
257
if self .redirect_stderr :
236
258
stderr_dest = subprocess .STDOUT
237
259
else :
238
260
stderr_event = threading .Event ()
239
261
stderr_thread = OutputThread (stderr_event , self .logger ,
240
- doprint = self .doprint )
262
+ doprint = self .doprint ,
263
+ max_lines = self .max_lines ,
264
+ max_line_length = self .max_line_length )
241
265
stderr_dest = stderr_thread
242
266
243
267
start_time = None
@@ -403,11 +427,18 @@ def getretcode(self):
403
427
404
428
def getoutputstr (self ):
405
429
if self .state == Command .FINISHED :
406
- return "" .join (self .out ).strip ()
430
+ s = os .linesep .join (self .out )
431
+ if self .out :
432
+ s += os .linesep
433
+ return s
407
434
else :
408
435
return None
409
436
410
437
def getoutput (self ):
438
+ """
439
+ :return: list of lines (with trailing whitespace and newlines stripped)
440
+ or None if the command has not finished yet
441
+ """
411
442
if self .state == Command .FINISHED :
412
443
return self .out
413
444
else :
@@ -418,7 +449,10 @@ def geterroutput(self):
418
449
419
450
def geterroutputstr (self ):
420
451
if self .err :
421
- return "" .join (self .err ).strip ()
452
+ s = os .linesep .join (self .err )
453
+ if len (self .err ) > 0 :
454
+ s += os .linesep
455
+ return s
422
456
else :
423
457
return ""
424
458
0 commit comments