def run(args: List[str],
        env: Dict[str, str] = None,
        capture_stdout: bool = False,
        echo_stdout: bool = True,
        capture_stderr: bool = False,
        echo_stderr: bool = True,
        debug_show_env: bool = True,
        encoding: str = sys.getdefaultencoding(),
        allow_failure: bool = False,
        **kwargs) -> Tuple[str, str]:
    """
    Runs an external process, announcing it.

    Optionally, retrieves its ``stdout`` and/or ``stderr`` output (if not
    retrieved, the output will be visible to the user).

    Args:
        args: list of command-line arguments (the first being the executable)

        env: operating system environment to use (if ``None``, the current OS
            environment will be used)

        capture_stdout: capture the command's ``stdout``?

        echo_stdout: allow the command's ``stdout`` to go to ``sys.stdout``?

        capture_stderr: capture the command's ``stderr``?

        echo_stderr: allow the command's ``stderr`` to go to ``sys.stderr``?

        debug_show_env: be verbose and show the environment used before calling

        encoding: encoding to use to translate the command's output

        allow_failure: if ``True``, continues if the command returns a
            non-zero (failure) exit code; if ``False``, raises an error if
            that happens

        kwargs: additional arguments to :func:`teed_call`

    Returns:
        a tuple: ``(stdout, stderr)``. If the output wasn't captured, an empty
        string will take its place in this tuple.
    """
    cwd = os.getcwd()
    # log.debug("External command Python form: {}", args)
    copy_paste_cmd = subprocess.list2cmdline(args)
    csep = "=" * 79
    esep = "-" * 79
    effective_env = env or os.environ
    if debug_show_env:
        log.debug(
            "Environment for the command that follows:\n"
            "{esep}\n"
            "{env}\n"
            "{esep}".format(esep=esep, env=make_copy_paste_env(effective_env))
        )
    log.info(
        "Launching external command:\n"
        "{csep}\n"
        "WORKING DIRECTORY: {cwd}\n"
        "PYTHON ARGS: {args!r}\n"
        "COMMAND: {cmd}\n"
        "{csep}".format(csep=csep, cwd=cwd, cmd=copy_paste_cmd,
                        args=args)
    )
    try:
        with io.StringIO() as out, io.StringIO() as err:
            stdout_targets = []  # type: List[TextIO]
            stderr_targets = []  # type: List[TextIO]
            if capture_stdout:
                stdout_targets.append(out)
            if echo_stdout:
                stdout_targets.append(sys.stdout)
            if capture_stderr:
                stderr_targets.append(err)
            if echo_stderr:
                stderr_targets.append(sys.stderr)
            retcode = teed_call(args,
                                stdout_targets=stdout_targets,
                                stderr_targets=stderr_targets,
                                encoding=encoding,
                                env=env,
                                **kwargs)
            stdout = out.getvalue()
            stderr = err.getvalue()
            if retcode != 0 and not allow_failure:
                # subprocess.check_call() and check_output() raise
                # CalledProcessError if the called process returns a non-zero
                # return code.
                raise subprocess.CalledProcessError(returncode=retcode,
                                                    cmd=args,
                                                    output=stdout,
                                                    stderr=stderr)
        log.debug("\n{csep}\nFINISHED SUCCESSFULLY: {cmd}\n{csep}",
                  cmd=copy_paste_cmd, csep=csep)
        return stdout, stderr
    except FileNotFoundError:
        require_executable(args[0])  # which is missing, so we'll see some help
        raise
    except subprocess.CalledProcessError:
        log.critical(
            "Command that failed:\n"
            "[ENVIRONMENT]\n"
            "{env}\n"
            "\n"
            "[DIRECTORY] {cwd}\n"
            "[PYTHON ARGS] {args}\n"
            "[COMMAND] {cmd}".format(
                cwd=cwd,
                env=make_copy_paste_env(effective_env),
                cmd=copy_paste_cmd,
                args=args
            )
        )
        raise
def run ( args : List [ str ] , env : Dict [ str , str ] = None , capture_stdout : bool = False , echo_stdout : bool = True , capture_stderr : bool = False , echo_stderr : bool = True , debug_show_env : bool = True , encoding : str = sys . getdefaultencoding ( ) , allow_failure : bool = False , ** kwargs ) -> Tuple [ str , str ] : cwd = os . getcwd ( ) copy_paste_cmd = subprocess . list2cmdline ( args ) csep = "=" * 79 esep = "-" * 79 effective_env = env or os . environ if debug_show_env : log . debug ( "Environment for the command that follows:\n" "{esep}\n" "{env}\n" "{esep}" . format ( esep = esep , env = make_copy_paste_env ( effective_env ) ) ) log . info ( "Launching external command:\n" "{csep}\n" "WORKING DIRECTORY: {cwd}\n" "PYTHON ARGS: {args!r}\n" "COMMAND: {cmd}\n" "{csep}" . format ( csep = csep , cwd = cwd , cmd = copy_paste_cmd , args = args ) ) try : with io . StringIO ( ) as out , io . StringIO ( ) as err : stdout_targets = [ ] stderr_targets = [ ] if capture_stdout : stdout_targets . append ( out ) if echo_stdout : stdout_targets . append ( sys . stdout ) if capture_stderr : stderr_targets . append ( err ) if echo_stderr : stderr_targets . append ( sys . stderr ) retcode = teed_call ( args , stdout_targets = stdout_targets , stderr_targets = stderr_targets , encoding = encoding , env = env , ** kwargs ) stdout = out . getvalue ( ) stderr = err . getvalue ( ) if retcode != 0 and not allow_failure : raise subprocess . CalledProcessError ( returncode = retcode , cmd = args , output = stdout , stderr = stderr ) log . debug ( "\n{csep}\nFINISHED SUCCESSFULLY: {cmd}\n{csep}" , cmd = copy_paste_cmd , csep = csep ) return stdout , stderr except FileNotFoundError : require_executable ( args [ 0 ] ) raise except subprocess . CalledProcessError : log . critical ( "Command that failed:\n" "[ENVIRONMENT]\n" "{env}\n" "\n" "[DIRECTORY] {cwd}\n" "[PYTHON ARGS] {args}\n" "[COMMAND] {cmd}" . format ( cwd = cwd , env = make_copy_paste_env ( effective_env ) , cmd = copy_paste_cmd , args = args ) ) raise
Runs an external process, announcing it.

    Optionally, retrieves its ``stdout`` and/or ``stderr`` output (if not
    retrieved, the output will be visible to the user).

    Args:
        args: list of command-line arguments (the first being the executable)

        env: operating system environment to use (if ``None``, the current OS
            environment will be used)

        capture_stdout: capture the command's ``stdout``?

        echo_stdout: allow the command's ``stdout`` to go to ``sys.stdout``?

        capture_stderr: capture the command's ``stderr``?

        echo_stderr: allow the command's ``stderr`` to go to ``sys.stderr``?

        debug_show_env: be verbose and show the environment used before calling

        encoding: encoding to use to translate the command's output

        allow_failure: if ``True``, continues if the command returns a
            non-zero (failure) exit code; if ``False``, raises an error if
            that happens

        kwargs: additional arguments to :func:`teed_call`

    Returns:
        a tuple: ``(stdout, stderr)``. If the output wasn't captured, an empty
        string will take its place in this tuple.