Cleanup the code in gclient_utils to standardize on CheckCall nomenclature.
Simplify code by removing fail_status
Rename print_messages to always
Simplify the doc.
Review URL: http://codereview.chromium.org/3104036
git-svn-id: svn://svn.chromium.org/chrome/trunk/tools/depot_tools@58201 0039d316-1c4b-4281-b951-d872f2087c98
diff --git a/gclient_utils.py b/gclient_utils.py
index e0c9e8e..66514f1 100644
--- a/gclient_utils.py
+++ b/gclient_utils.py
@@ -118,6 +118,7 @@
class Error(Exception):
"""gclient exception class."""
+ # TODO(maruel): Merge with CheckCallError.
pass
@@ -251,54 +252,56 @@
os.rmdir(file_path)
-def SubprocessCall(args, **kwargs):
- """Wraps SubprocessCallAndFilter() with different default arguments.
+def CheckCallAndFilterAndHeader(args, always=False, **kwargs):
+ """Adds 'header' support to CheckCallAndFilter.
- Calls subprocess and capture nothing."""
- kwargs['print_messages'] = True
- kwargs['print_stdout'] = True
- return SubprocessCallAndFilter(args, **kwargs)
-
-
-def SubprocessCallAndFilter(args, **kwargs):
- """Runs a command and prints a header line if appropriate.
-
- If |print_messages| is True, a message indicating what is being done
- is printed to stdout. Otherwise the message is printed only if the call
- generated any ouput. If both |print_messages| and |print_stdout| are False,
- no output at all is generated.
-
- If |print_stdout| is True, the command's stdout is also forwarded to stdout.
-
- If |filter_fn| function is specified, it is expected to take a single
- string argument, and it will be called with each line of the
- subprocess's output. Each line has had the trailing newline character
- trimmed.
-
- If the command fails, as indicated by a nonzero exit status, gclient will
- exit with an exit status of fail_status. If fail_status is None (the
- default), gclient will raise an Error exception.
-
- Other subprocess.Popen parameters can be specified.
+ 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
+ the message header is printed only if the call generated any ouput.
"""
- stdout = kwargs.pop('stdout', sys.stdout) or sys.stdout
- assert not 'stderr' in kwargs
- filter_fn = kwargs.pop('filter_fn', None)
- print_messages = kwargs.pop('print_messages', False)
- print_stdout = kwargs.pop('print_stdout', False)
- fail_status = kwargs.pop('fail_status', None)
-
- logging.debug(args)
- if print_messages:
+ stdout = kwargs.get('stdout', None) or sys.stdout
+ if always:
stdout.write('\n________ running \'%s\' in \'%s\'\n'
- % (' '.join(args), kwargs['cwd']))
+ % (' '.join(args), kwargs.get('cwd', '.')))
+ else:
+ filter_fn = kwargs.get('filter_fn', None)
+ def filter_msg(line):
+ if line is None:
+ stdout.write('\n________ running \'%s\' in \'%s\'\n'
+ % (' '.join(args), kwargs.get('cwd', '.')))
+ elif filter_fn:
+ filter_fn(line)
+ kwargs['filter_fn'] = filter_msg
+ kwargs['call_filter_on_first_line'] = True
+ # Obviously.
+ kwargs['print_stdout'] = True
+ return CheckCallAndFilter(args, **kwargs)
+
+def CheckCallAndFilter(args, stdout=None, filter_fn=None,
+ print_stdout=None, call_filter_on_first_line=False,
+ **kwargs):
+ """Runs a command and calls back a filter function if needed.
+
+ Accepts all subprocess.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 subprocess's output. Each line has the trailing newline
+ character trimmed.
+ stdout: Can be any bufferable output.
+
+ stderr is always redirected to stdout.
+ """
+ assert print_stdout or filter_fn
+ stdout = stdout or sys.stdout
+ filter_fn = filter_fn or (lambda x: None)
+ assert not 'stderr' in kwargs
+ logging.debug(args)
kid = Popen(args, bufsize=0,
stdout=subprocess.PIPE, stderr=subprocess.STDOUT,
**kwargs)
- # Do a flush of sys.stdout before we begin reading from the subprocess's
- # stdout.
+ # Do a flush of stdout before we begin reading from the subprocess's stdout
last_flushed_at = time.time()
stdout.flush()
@@ -307,40 +310,34 @@
# normally buffering is done for each line, but if svn requests input, no
# end-of-line character is output after the prompt and it would not show up.
in_byte = kid.stdout.read(1)
- in_line = ''
- while in_byte:
- if in_byte != '\r':
- if print_stdout:
- if not print_messages:
- stdout.write('\n________ running \'%s\' in \'%s\'\n'
- % (' '.join(args), kwargs['cwd']))
- print_messages = True
- stdout.write(in_byte)
- if in_byte != '\n':
- in_line += in_byte
- if in_byte == '\n':
- if filter_fn:
- filter_fn(in_line)
- in_line = ''
- # Flush at least 10 seconds between line writes. We wait at least 10
- # seconds to avoid overloading the reader that called us with output,
- # which can slow busy readers down.
- if (time.time() - last_flushed_at) > 10:
- last_flushed_at = time.time()
- stdout.flush()
- in_byte = kid.stdout.read(1)
- # Flush the rest of buffered output. This is only an issue with files not
- # ending with a \n.
- if len(in_line) and filter_fn:
- filter_fn(in_line)
+ if in_byte:
+ if call_filter_on_first_line:
+ filter_fn(None)
+ in_line = ''
+ while in_byte:
+ if in_byte != '\r':
+ if print_stdout:
+ stdout.write(in_byte)
+ if in_byte != '\n':
+ in_line += in_byte
+ else:
+ filter_fn(in_line)
+ in_line = ''
+ # Flush at least 10 seconds between line writes. We wait at least 10
+ # seconds to avoid overloading the reader that called us with output,
+ # which can slow busy readers down.
+ if (time.time() - last_flushed_at) > 10:
+ last_flushed_at = time.time()
+ stdout.flush()
+ in_byte = kid.stdout.read(1)
+ # 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 = kid.wait()
-
if rv:
- msg = 'failed to run command: %s' % ' '.join(args)
- if fail_status != None:
- sys.stderr.write(msg + '\n')
- sys.exit(fail_status)
- raise Error(msg)
+ raise Error('failed to run command: %s' % ' '.join(args))
+ return 0
def FindGclientRoot(from_dir, filename='.gclient'):