| Index: gclient_utils.py
|
| diff --git a/gclient_utils.py b/gclient_utils.py
|
| index 556018105cc8cd0c30e42f0ec6e235efbe900eeb..93c0a7e46cba08364401c9d6cc995fc9f212f153 100644
|
| --- a/gclient_utils.py
|
| +++ b/gclient_utils.py
|
| @@ -21,7 +21,9 @@ import threading
|
| import time
|
| import urlparse
|
|
|
| +# TODO(tandrii): migrate users to subprocess42.
|
| import subprocess2
|
| +import subprocess42
|
|
|
|
|
| RETRY_MAX = 3
|
| @@ -269,8 +271,10 @@ def CheckCallAndFilterAndHeader(args, always=False, header=None, **kwargs):
|
| """Adds 'header' support to CheckCallAndFilter.
|
|
|
| If |always| is True, a message indicating what is being done
|
| - is printed to stdout all the time even if not output is generated. Otherwise
|
| + is printed to stdout all the time even if no output is generated. Otherwise
|
| the message header is printed only if the call generated any ouput.
|
| +
|
| + If use_v42=True is given, uses subprocess42 through CheckCallAndFilter42.
|
| """
|
| stdout = kwargs.setdefault('stdout', sys.stdout)
|
| if header is None:
|
| @@ -290,6 +294,8 @@ def CheckCallAndFilterAndHeader(args, always=False, header=None, **kwargs):
|
| kwargs['call_filter_on_first_line'] = True
|
| # Obviously.
|
| kwargs.setdefault('print_stdout', True)
|
| + if kwargs.pop('use_v42', False):
|
| + return CheckCallAndFilter42(args, **kwargs)
|
| return CheckCallAndFilter(args, **kwargs)
|
|
|
|
|
| @@ -521,6 +527,7 @@ class _KillTimer(object):
|
| return
|
|
|
|
|
| +#TODO(tandrii): all users of this should be converted to CheckCallAndFilter42.
|
| def CheckCallAndFilter(args, stdout=None, filter_fn=None,
|
| print_stdout=None, call_filter_on_first_line=False,
|
| retry=False, kill_timeout=None, **kwargs):
|
| @@ -613,6 +620,104 @@ def CheckCallAndFilter(args, stdout=None, filter_fn=None,
|
| rv, args, kwargs.get('cwd', None), None, None)
|
|
|
|
|
| +def CheckCallAndFilter42(args, stdout=None, filter_fn=None,
|
| + print_stdout=None, call_filter_on_first_line=False,
|
| + retry=False, kill_timeout=None, **kwargs):
|
| + """Runs a command and calls back a filter function if needed.
|
| +
|
| + Accepts all subprocess2.Popen() parameters plus:
|
| + print_stdout: If True, the command's stdout is forwarded to stdout.
|
| + filter_fn: A function taking a single string argument called with each line
|
| + of the subprocess2's output. Each line has the trailing newline
|
| + character trimmed.
|
| + stdout: Can be any bufferable output.
|
| + retry: If the process exits non-zero, sleep for a brief interval and try
|
| + again, up to RETRY_MAX times.
|
| + kill_timeout: (float) if given, number of seconds after which spawned
|
| + process would be killed. This applies to each retry separately.
|
| +
|
| + stderr is always redirected to stdout.
|
| + """
|
| + assert print_stdout or filter_fn
|
| + if kill_timeout:
|
| + assert isinstance(kill_timeout, (int, float)), kill_timeout
|
| +
|
| + stdout = stdout or sys.stdout
|
| + output = cStringIO.StringIO()
|
| + filter_fn = filter_fn or (lambda x: None)
|
| +
|
| + sleep_interval = RETRY_INITIAL_SLEEP
|
| + run_cwd = kwargs.get('cwd', os.getcwd())
|
| + debug_child_info = "'%s' in %s" % (' '.join('"%s"' % x for x in args), run_cwd)
|
| +
|
| + for _ in xrange(RETRY_MAX + 1):
|
| + child = subprocess42.Popen(
|
| + args, bufsize=0, stdout=subprocess2.PIPE, stderr=subprocess2.STDOUT,
|
| + **kwargs)
|
| +
|
| + GClientChildren.add(child)
|
| +
|
| + # Do a flush of stdout before we begin reading from the subprocess42's
|
| + # stdout.
|
| + stdout.flush()
|
| +
|
| + # Also, we need to forward stdout to prevent weird re-ordering of output.
|
| + try:
|
| + first_line = True
|
| + in_line = ''
|
| + # Don't block for more than 1 second.
|
| + for pipe, data in child.yield_any(maxsize=256, timeout=1):
|
| + if pipe is None:
|
| + # No new data.
|
| + if kill_timeout and child.duration() > kill_timeout:
|
| + print('ERROR: killing process %s running for %.0fs (timeout: %.0fs)'
|
| + % (debug_child_info, child.duration(), kill_timeout))
|
| + child.kill()
|
| + continue
|
| +
|
| + assert pipe == 'stdout'
|
| + if first_line:
|
| + first_line = False
|
| + if call_filter_on_first_line:
|
| + filter_fn(None)
|
| +
|
| + output.write(data)
|
| + if print_stdout:
|
| + stdout.write(data)
|
| + for byte in data:
|
| + if byte not in ['\r', '\n']:
|
| + in_line += byte
|
| + else:
|
| + filter_fn(in_line)
|
| + in_line = ''
|
| + # Flush the rest of buffered output. This is only an issue with
|
| + # stdout/stderr not ending with a \n.
|
| + if len(in_line):
|
| + filter_fn(in_line)
|
| + rv = child.wait()
|
| +
|
| + # Don't put this in a 'finally,' since the child may still run if we get
|
| + # an exception.
|
| + GClientChildren.remove(child)
|
| +
|
| + except KeyboardInterrupt:
|
| + print >> sys.stderr, 'Failed while running "%s"' % ' '.join(args)
|
| + raise
|
| +
|
| + if rv == 0:
|
| + return output.getvalue()
|
| + if not retry:
|
| + break
|
| + print ("WARNING: subprocess %s failed; will retry after a short nap..." %
|
| + debug_child_info)
|
| + time.sleep(sleep_interval)
|
| + sleep_interval *= 2
|
| + # TODO(tandrii): change this to subprocess.CalledProcessError,
|
| + # beacuse subprocess42 doesn't have a class unlike subprocess2.
|
| + raise subprocess2.CalledProcessError(
|
| + rv, args, kwargs.get('cwd', None), None, None)
|
| +
|
| +
|
| class GitFilter(object):
|
| """A filter_fn implementation for quieting down git output messages.
|
|
|
|
|