scripts/release/util.py
changeset 1835 3f30b7b14c57
parent 1833 9df2e9a67081
child 1846 ac30e04bcbba
equal deleted inserted replaced
1834:0589bf1395c5 1835:3f30b7b14c57
    31 
    31 
    32 import error
    32 import error
    33 
    33 
    34 
    34 
    35 class Error(error.Error):
    35 class Error(error.Error):
    36     pass
    36   pass
    37 
    37 
    38 
    38 
    39 class SubprocessFailed(Error):
    39 class SubprocessFailed(Error):
    40     """A subprocess returned a non-zero error code."""
    40   """A subprocess returned a non-zero error code."""
    41 
    41 
    42 
    42 
    43 # The magic escape sequence understood by modern terminal emulators to
    43 # The magic escape sequence understood by modern terminal emulators to
    44 # configure fore/background colors and other basic text display
    44 # configure fore/background colors and other basic text display
    45 # settings.
    45 # settings.
    57 GREEN = 32
    57 GREEN = 32
    58 WHITE = 37
    58 WHITE = 37
    59 
    59 
    60 
    60 
    61 def _ansi_escape(code):
    61 def _ansi_escape(code):
    62     return _ANSI_ESCAPE % code
    62   return _ANSI_ESCAPE % code
    63 
    63 
    64 
    64 
    65 def colorize(text, color, bold=False):
    65 def colorize(text, color, bold=False):
    66     """Colorize some text using ANSI color codes.
    66   """Colorize some text using ANSI color codes.
    67 
    67 
    68     Note that while ANSI color codes look good in a terminal they look
    68   Note that while ANSI color codes look good in a terminal they look
    69     like noise in log files unless viewed in an ANSI color capable
    69   like noise in log files unless viewed in an ANSI color capable
    70     viewer (such as 'less -R').
    70   viewer (such as 'less -R').
    71 
    71 
    72     Args:
    72   Args:
    73       text: The text to colorize.
    73     text: The text to colorize.
    74       color: One of the color symbols from this module.
    74     color: One of the color symbols from this module.
    75       bold: If True, make the color brighter.
    75     bold: If True, make the color brighter.
    76 
    76 
    77     Returns:
    77   Returns:
    78       The input text string, appropriately sprinkled with color
    78     The input text string, appropriately sprinkled with color
    79       codes. Colors are reset to terminal defaults after the input
    79     codes. Colors are reset to terminal defaults after the input
    80       text.
    80     text.
    81     """
    81   """
    82     bold = _ansi_escape(_BOLD) if bold else ''
    82   bold = _ansi_escape(_BOLD) if bold else ''
    83     return '%s%s%s%s' % (bold, _ansi_escape(color),
    83   return '%s%s%s%s' % (bold, _ansi_escape(color),
    84                          text, _ansi_escape(_RESET))
    84                        text, _ansi_escape(_RESET))
    85 
    85 
    86 
    86 
    87 def decolorize(text):
    87 def decolorize(text):
    88     """Remove ANSI color codes from text."""
    88   """Remove ANSI color codes from text."""
    89     return _ANSI_ESCAPE_RE.sub('', text)
    89   return _ANSI_ESCAPE_RE.sub('', text)
    90 
    90 
    91 
    91 
    92 class Paths(object):
    92 class Paths(object):
    93     """A helper to construct and check paths under a given root."""
    93   """A helper to construct and check paths under a given root."""
    94 
    94 
    95     def __init__(self, root):
    95   def __init__(self, root):
    96         """Initializer.
    96     """Initializer.
    97 
    97 
    98         Args:
    98     Args:
    99           root: The root of all paths this instance will consider.
    99       root: The root of all paths this instance will consider.
   100         """
   100     """
   101         self._root = os.path.abspath(
   101     self._root = os.path.abspath(
   102             os.path.expandvars(os.path.expanduser(root)))
   102       os.path.expandvars(os.path.expanduser(root)))
   103 
   103 
   104     def path(self, path=''):
   104   def path(self, path=''):
   105         """Construct and return a path under the path root.
   105     """Construct and return a path under the path root.
   106 
   106 
   107         Args:
   107     Args:
   108           path: The desired path string relative to the root.
   108       path: The desired path string relative to the root.
   109 
   109 
   110         Returns:
   110     Returns:
   111           The absolute path corresponding to the relative input path.
   111       The absolute path corresponding to the relative input path.
   112         """
   112     """
   113         assert not os.path.isabs(path)
   113     assert not os.path.isabs(path)
   114         return os.path.abspath(os.path.join(self._root, path))
   114     return os.path.abspath(os.path.join(self._root, path))
   115 
   115 
   116     def exists(self, path=''):
   116   def exists(self, path=''):
   117         """Check for the existence of a path under the path root.
   117     """Check for the existence of a path under the path root.
   118 
   118 
   119         Does not discriminate on the path type (ie. it could be a
   119     Does not discriminate on the path type (ie. it could be a
   120         directory, a file, a symbolic link...), just checks for the
   120     directory, a file, a symbolic link...), just checks for the
   121         existence of the path.
   121     existence of the path.
   122 
   122 
   123         Args:
   123     Args:
   124           path: The path string relative to the root.
   124       path: The path string relative to the root.
   125 
   125 
   126         Returns:
   126     Returns:
   127           True if the path exists, False otherwise.
   127       True if the path exists, False otherwise.
   128         """
   128     """
   129         return os.path.exists(self.path(path))
   129     return os.path.exists(self.path(path))
   130 
   130 
   131 
   131 
   132 def run(argv, cwd=None, capture=False, split_capture=True, stdin=''):
   132 def run(argv, cwd=None, capture=False, split_capture=True, stdin=''):
   133     """Run the given command and optionally return its output.
   133   """Run the given command and optionally return its output.
   134 
   134 
   135     Note that if you set capture=True, the command's output is
   135   Note that if you set capture=True, the command's output is
   136     buffered in memory. Output capture should only be used with
   136   buffered in memory. Output capture should only be used with
   137     commands that output small amounts of data. O(kB) is fine, O(MB)
   137   commands that output small amounts of data. O(kB) is fine, O(MB)
   138     is starting to push it a little.
   138   is starting to push it a little.
   139 
   139 
   140     Args:
   140   Args:
   141       argv: A list containing the name of the program to run, followed
   141     argv: A list containing the name of the program to run, followed
   142             by its argument vector.
   142       by its argument vector.
   143       cwd: Run the program from this directory.
   143     cwd: Run the program from this directory.
   144       capture: If True, capture the program's stdout stream. If False,
   144     capture: If True, capture the program's stdout stream. If False,
   145                stdout will output to sys.stdout.
   145          stdout will output to sys.stdout.
   146       split_capture: If True, return the captured output as a list of
   146     split_capture: If True, return the captured output as a list of
   147                      lines. Else, return as a single unaltered string.
   147            lines. Else, return as a single unaltered string.
   148       stdin: The string to feed to the program's stdin stream.
   148     stdin: The string to feed to the program's stdin stream.
   149 
   149 
   150     Returns:
   150   Returns:
   151       If capture is True, a string containing the combined
   151     If capture is True, a string containing the combined
   152       stdout/stderr output of the program. If capture is False,
   152     stdout/stderr output of the program. If capture is False,
   153       nothing is returned.
   153     nothing is returned.
   154 
   154 
   155     Raises:
   155   Raises:
   156       SubprocessFailed: The subprocess exited with a non-zero exit
   156     SubprocessFailed: The subprocess exited with a non-zero exit
   157                         code.
   157             code.
   158     """
   158   """
   159     print colorize('# ' + ' '.join(argv), WHITE, bold=True)
   159   print colorize('# ' + ' '.join(argv), WHITE, bold=True)
   160 
   160 
   161     process = subprocess.Popen(argv,
   161   process = subprocess.Popen(argv,
   162                                shell=False,
   162                              shell=False,
   163                                cwd=cwd,
   163                              cwd=cwd,
   164                                stdin=subprocess.PIPE,
   164                              stdin=subprocess.PIPE,
   165                                stdout=(subprocess.PIPE if capture else None),
   165                              stdout=(subprocess.PIPE if capture else None),
   166                                stderr=None)
   166                              stderr=None)
   167     output, _ = process.communicate(input=stdin)
   167   output, _ = process.communicate(input=stdin)
   168     if process.returncode != 0:
   168   if process.returncode != 0:
   169         raise SubprocessFailed('Process %s failed with output: %s' %
   169     raise SubprocessFailed('Process %s failed with output: %s' %
   170                                (argv[0], output))
   170                            (argv[0], output))
   171     if output is not None and split_capture:
   171   if output is not None and split_capture:
   172         return output.strip().split('\n')
   172     return output.strip().split('\n')
   173     else:
   173   else:
   174         return output
   174     return output