|  | @@ -33,6 +33,7 @@ import hashlib
 | 
	
		
			
				|  |  |  import multiprocessing
 | 
	
		
			
				|  |  |  import os
 | 
	
		
			
				|  |  |  import random
 | 
	
		
			
				|  |  | +import signal
 | 
	
		
			
				|  |  |  import subprocess
 | 
	
		
			
				|  |  |  import sys
 | 
	
		
			
				|  |  |  import tempfile
 | 
	
	
		
			
				|  | @@ -42,6 +43,12 @@ import time
 | 
	
		
			
				|  |  |  _DEFAULT_MAX_JOBS = 16 * multiprocessing.cpu_count()
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +# setup a signal handler so that signal.pause registers 'something'
 | 
	
		
			
				|  |  | +# when a child finishes
 | 
	
		
			
				|  |  | +# not using futures and threading to avoid a dependency on subprocess32
 | 
	
		
			
				|  |  | +signal.signal(signal.SIGCHLD, lambda unused_signum, unused_frame: None)
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |  def shuffle_iteratable(it):
 | 
	
		
			
				|  |  |    """Return an iterable that randomly walks it"""
 | 
	
		
			
				|  |  |    # take a random sampling from the passed in iterable
 | 
	
	
		
			
				|  | @@ -94,16 +101,19 @@ _TAG_COLOR = {
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  def message(tag, message, explanatory_text=None, do_newline=False):
 | 
	
		
			
				|  |  | -  sys.stdout.write('%s%s%s\x1b[%d;%dm%s\x1b[0m: %s%s' % (
 | 
	
		
			
				|  |  | -      _BEGINNING_OF_LINE,
 | 
	
		
			
				|  |  | -      _CLEAR_LINE,
 | 
	
		
			
				|  |  | -      '\n%s' % explanatory_text if explanatory_text is not None else '',
 | 
	
		
			
				|  |  | -      _COLORS[_TAG_COLOR[tag]][1],
 | 
	
		
			
				|  |  | -      _COLORS[_TAG_COLOR[tag]][0],
 | 
	
		
			
				|  |  | -      tag,
 | 
	
		
			
				|  |  | -      message,
 | 
	
		
			
				|  |  | -      '\n' if do_newline or explanatory_text is not None else ''))
 | 
	
		
			
				|  |  | -  sys.stdout.flush()
 | 
	
		
			
				|  |  | +  try:
 | 
	
		
			
				|  |  | +    sys.stdout.write('%s%s%s\x1b[%d;%dm%s\x1b[0m: %s%s' % (
 | 
	
		
			
				|  |  | +        _BEGINNING_OF_LINE,
 | 
	
		
			
				|  |  | +        _CLEAR_LINE,
 | 
	
		
			
				|  |  | +        '\n%s' % explanatory_text if explanatory_text is not None else '',
 | 
	
		
			
				|  |  | +        _COLORS[_TAG_COLOR[tag]][1],
 | 
	
		
			
				|  |  | +        _COLORS[_TAG_COLOR[tag]][0],
 | 
	
		
			
				|  |  | +        tag,
 | 
	
		
			
				|  |  | +        message,
 | 
	
		
			
				|  |  | +        '\n' if do_newline or explanatory_text is not None else ''))
 | 
	
		
			
				|  |  | +    sys.stdout.flush()
 | 
	
		
			
				|  |  | +  except:
 | 
	
		
			
				|  |  | +    pass
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  def which(filename):
 | 
	
	
		
			
				|  | @@ -232,7 +242,7 @@ class Jobset(object):
 | 
	
		
			
				|  |  |        if dead: return
 | 
	
		
			
				|  |  |        message('WAITING', '%d jobs running, %d complete, %d failed' % (
 | 
	
		
			
				|  |  |            len(self._running), self._completed, self._failures))
 | 
	
		
			
				|  |  | -      time.sleep(0.1)
 | 
	
		
			
				|  |  | +      signal.pause()
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |    def cancelled(self):
 | 
	
		
			
				|  |  |      """Poll for cancellation."""
 |