def call_subprocess(<EOL>cmd,  <EOL>show_stdout=True,  <EOL>cwd=None,  <EOL>on_returncode='<STR_LIT>',  <EOL>extra_ok_returncodes=None,  <EOL>command_desc=None,  <EOL>extra_environ=None,  <EOL>unset_environ=None,  <EOL>spinner=None  <EOL>):<EOL>
if extra_ok_returncodes is None:<EOL><INDENT>extra_ok_returncodes = []<EOL><DEDENT>if unset_environ is None:<EOL><INDENT>unset_environ = []<EOL><DEDENT>if show_stdout:<EOL><INDENT>stdout = None<EOL><DEDENT>else:<EOL><INDENT>stdout = subprocess.PIPE<EOL><DEDENT>if command_desc is None:<EOL><INDENT>cmd_parts = []<EOL>for part in cmd:<EOL><INDENT>if '<STR_LIT:U+0020>' in part or '<STR_LIT:\n>' in part or '<STR_LIT:">' in part or "<STR_LIT:'>" in part:<EOL><INDENT>part = '<STR_LIT>' % part.replace('<STR_LIT:">', '<STR_LIT>')<EOL><DEDENT>cmd_parts.append(part)<EOL><DEDENT>command_desc = '<STR_LIT:U+0020>'.join(cmd_parts)<EOL><DEDENT>logger.debug("<STR_LIT>", command_desc)<EOL>env = os.environ.copy()<EOL>if extra_environ:<EOL><INDENT>env.update(extra_environ)<EOL><DEDENT>for name in unset_environ:<EOL><INDENT>env.pop(name, None)<EOL><DEDENT>try:<EOL><INDENT>proc = subprocess.Popen(<EOL>cmd, stderr=subprocess.STDOUT, stdin=subprocess.PIPE,<EOL>stdout=stdout, cwd=cwd, env=env,<EOL>)<EOL>proc.stdin.close()<EOL><DEDENT>except Exception as exc:<EOL><INDENT>logger.critical(<EOL>"<STR_LIT>", exc, command_desc,<EOL>)<EOL>raise<EOL><DEDENT>all_output = []<EOL>if stdout is not None:<EOL><INDENT>while True:<EOL><INDENT>line = console_to_str(proc.stdout.readline())<EOL>if not line:<EOL><INDENT>break<EOL><DEDENT>line = line.rstrip()<EOL>all_output.append(line + '<STR_LIT:\n>')<EOL>if logger.getEffectiveLevel() <= std_logging.DEBUG:<EOL><INDENT>logger.debug(line)<EOL><DEDENT>else:<EOL><INDENT>if spinner is not None:<EOL><INDENT>spinner.spin()<EOL><DEDENT><DEDENT><DEDENT><DEDENT>try:<EOL><INDENT>proc.wait()<EOL><DEDENT>finally:<EOL><INDENT>if proc.stdout:<EOL><INDENT>proc.stdout.close()<EOL><DEDENT><DEDENT>if spinner is not None:<EOL><INDENT>if proc.returncode:<EOL><INDENT>spinner.finish("<STR_LIT:error>")<EOL><DEDENT>else:<EOL><INDENT>spinner.finish("<STR_LIT>")<EOL><DEDENT><DEDENT>if proc.returncode and proc.returncode not in extra_ok_returncodes:<EOL><INDENT>if on_returncode == '<STR_LIT>':<EOL><INDENT>if (logger.getEffectiveLevel() > std_logging.DEBUG and<EOL>not show_stdout):<EOL><INDENT>logger.info(<EOL>'<STR_LIT>', command_desc,<EOL>)<EOL>logger.info(<EOL>'<STR_LIT>'.join(all_output) +<EOL>'<STR_LIT>'<EOL>)<EOL><DEDENT>raise InstallationError(<EOL>'<STR_LIT>'<EOL>% (command_desc, proc.returncode, cwd))<EOL><DEDENT>elif on_returncode == '<STR_LIT>':<EOL><INDENT>logger.warning(<EOL>'<STR_LIT>',<EOL>command_desc, proc.returncode, cwd,<EOL>)<EOL><DEDENT>elif on_returncode == '<STR_LIT:ignore>':<EOL><INDENT>pass<EOL><DEDENT>else:<EOL><INDENT>raise ValueError('<STR_LIT>' %<EOL>repr(on_returncode))<EOL><DEDENT><DEDENT>if not show_stdout:<EOL><INDENT>return '<STR_LIT>'.join(all_output)<EOL><DEDENT>return None<EOL>
Args:
  extra_ok_returncodes: an iterable of integer return codes that are
    acceptable, in addition to 0. Defaults to None, which means [].
  unset_environ: an iterable of environment variable names to unset
    prior to calling subprocess.Popen().