File size: 1,600 Bytes
6cc35b0
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
"""Filesystem and subprocess types used by optional structure utilities."""

from __future__ import annotations

import io
import subprocess
from pathlib import Path
from typing import Any, TypeAlias

PathLike: TypeAlias = str | Path
PathOrBuffer: TypeAlias = PathLike | io.StringIO


def _stdout_destination(*, capture_output: bool, quiet: bool) -> int | None:
    if capture_output:
        return subprocess.PIPE
    if quiet:
        return subprocess.DEVNULL
    return None


def _stderr_text(error: subprocess.CalledProcessError) -> str:
    stderr = error.stderr
    if stderr is None:
        return ""
    if isinstance(stderr, bytes):
        return stderr.decode(errors="replace")
    return str(stderr)


def run_subprocess_with_errorcheck(
    *popenargs: Any,
    capture_output: bool = False,
    quiet: bool = False,
    env: dict[str, str] | None = None,
    shell: bool = False,
    executable: str | None = None,
    **kwargs: Any,
) -> subprocess.CompletedProcess[Any]:
    """Run a command and include captured standard error in failures."""

    stdout = _stdout_destination(capture_output=capture_output, quiet=quiet)
    try:
        return subprocess.run(
            *popenargs,
            check=True,
            env=env,
            executable=executable,
            shell=shell,
            stderr=subprocess.PIPE,
            stdout=stdout,
            **kwargs,
        )
    except subprocess.CalledProcessError as error:
        message = f"Command failed with errorcode {error.returncode}.\n\n{_stderr_text(error)}"
        raise RuntimeError(message) from error