scripts/release/util.py
changeset 1827 c03995a6a88e
parent 1826 12de6d73a908
child 1833 9df2e9a67081
equal deleted inserted replaced
1826:12de6d73a908 1827:c03995a6a88e
    15 """Various utilities.
    15 """Various utilities.
    16 
    16 
    17 Current contents:
    17 Current contents:
    18  - Text colorization using ANSI color codes
    18  - Text colorization using ANSI color codes
    19  - A class to construct and manage paths under a root path.
    19  - A class to construct and manage paths under a root path.
       
    20  - A helper to manage running subprocesses, wrapping the subprocess module.
    20 """
    21 """
    21 
    22 
    22 __authors__ = [
    23 __authors__ = [
    23     # alphabetical order by last name, please
    24     # alphabetical order by last name, please
    24     '"David Anderson" <dave@natulte.net>',
    25     '"David Anderson" <dave@natulte.net>',
    25     ]
    26     ]
    26 
    27 
    27 import os.path
    28 import os.path
       
    29 import subprocess
       
    30 
       
    31 import error
       
    32 
       
    33 
       
    34 class Error(error.Error):
       
    35     pass
       
    36 
       
    37 
       
    38 class SubprocessFailed(Error):
       
    39     """A subprocess returned a non-zero error code."""
    28 
    40 
    29 
    41 
    30 # The magic escape sequence understood by modern terminal emulators to
    42 # The magic escape sequence understood by modern terminal emulators to
    31 # configure fore/background colors and other basic text display
    43 # configure fore/background colors and other basic text display
    32 # settings.
    44 # settings.
   106 
   118 
   107         Returns:
   119         Returns:
   108           True if the path exists, False otherwise.
   120           True if the path exists, False otherwise.
   109         """
   121         """
   110         return os.path.exists(self.path(path))
   122         return os.path.exists(self.path(path))
       
   123 
       
   124 
       
   125 def run(argv, cwd=None, capture=False, split_capture=True, stdin=''):
       
   126     """Run the given command and optionally return its output.
       
   127 
       
   128     Note that if you set capture=True, the command's output is
       
   129     buffered in memory. Output capture should only be used with
       
   130     commands that output small amounts of data. O(kB) is fine, O(MB)
       
   131     is starting to push it a little.
       
   132 
       
   133     Args:
       
   134       argv: A list containing the name of the program to run, followed
       
   135             by its argument vector.
       
   136       cwd: Run the program from this directory.
       
   137       capture: If True, capture the program's stdout stream. If False,
       
   138                stdout will output to sys.stdout.
       
   139       split_capture: If True, return the captured output as a list of
       
   140                      lines. Else, return as a single unaltered string.
       
   141       stdin: The string to feed to the program's stdin stream.
       
   142 
       
   143     Returns:
       
   144       If capture is True, a string containing the combined
       
   145       stdout/stderr output of the program. If capture is False,
       
   146       nothing is returned.
       
   147 
       
   148     Raises:
       
   149       SubprocessFailed: The subprocess exited with a non-zero exit
       
   150                         code.
       
   151     """
       
   152     print colorize('# ' + ' '.join(argv), WHITE, bold=True)
       
   153 
       
   154     process = subprocess.Popen(argv,
       
   155                                shell=False,
       
   156                                cwd=cwd,
       
   157                                stdin=subprocess.PIPE,
       
   158                                stdout=(subprocess.PIPE if capture else None),
       
   159                                stderr=None)
       
   160     output, _ = process.communicate(input=stdin)
       
   161     if process.returncode != 0:
       
   162         raise SubprocessFailed('Process %s failed with output: %s' %
       
   163                                (argv[0], output))
       
   164     if output is not None and split_capture:
       
   165         return output.strip().split('\n')
       
   166     else:
       
   167         return output