crytic_compile.utils.subprocess

Process execution helpers.

 1"""
 2Process execution helpers.
 3"""
 4import logging
 5import os
 6from pathlib import Path
 7import shutil
 8import subprocess
 9from typing import Any, Dict, List, Optional, Union
10
11LOGGER = logging.getLogger("CryticCompile")
12
13
14def run(
15    cmd: List[str],
16    cwd: Optional[Union[str, os.PathLike]] = None,
17    extra_env: Optional[Dict[str, str]] = None,
18    **kwargs: Any,
19) -> Optional[subprocess.CompletedProcess]:
20    """
21    Execute a command in a cross-platform compatible way.
22
23    Args:
24        cmd (List[str]): Command to run
25        cwd (PathLike): Working directory to run the command in
26        extra_env (Dict[str, str]): extra environment variables to define for the execution
27        **kwargs: optional arguments passed to `subprocess.run`
28
29    Returns:
30        CompletedProcess: If the execution succeeded
31        None: if there was a problem executing
32    """
33    subprocess_cwd = Path(os.getcwd() if cwd is None else cwd).resolve()
34    subprocess_env = None if extra_env is None else dict(os.environ, **extra_env)
35    subprocess_exe = shutil.which(cmd[0])
36
37    if subprocess_exe is None:
38        LOGGER.error("Cannot execute `%s`, is it installed and in PATH?", cmd[0])
39        return None
40
41    LOGGER.info(
42        "'%s' running (wd: %s)",
43        " ".join(cmd),
44        subprocess_cwd,
45    )
46
47    try:
48        return subprocess.run(
49            cmd,
50            executable=subprocess_exe,
51            cwd=subprocess_cwd,
52            env=subprocess_env,
53            check=True,
54            capture_output=True,
55            **kwargs,
56        )
57    except FileNotFoundError:
58        LOGGER.error("Could not execute `%s`, is it installed and in PATH?", cmd[0])
59    except subprocess.CalledProcessError as e:
60        LOGGER.error("'%s' returned non-zero exit code %d", cmd[0], e.returncode)
61        stdout, stderr = (
62            e.stdout.decode(errors="backslashreplace").strip(),
63            e.stderr.decode(errors="backslashreplace").strip(),
64        )
65        if stdout:
66            LOGGER.error("\nstdout: ".join(stdout.split("\n")))
67        if stderr:
68            LOGGER.error("\nstderr: ".join(stderr.split("\n")))
69    except OSError:
70        LOGGER.error("OS error executing:", exc_info=True)
71
72    return None
LOGGER = <Logger CryticCompile (WARNING)>
def run( cmd: List[str], cwd: Union[str, os.PathLike, NoneType] = None, extra_env: Union[Dict[str, str], NoneType] = None, **kwargs: Any) -> Union[subprocess.CompletedProcess, NoneType]:
15def run(
16    cmd: List[str],
17    cwd: Optional[Union[str, os.PathLike]] = None,
18    extra_env: Optional[Dict[str, str]] = None,
19    **kwargs: Any,
20) -> Optional[subprocess.CompletedProcess]:
21    """
22    Execute a command in a cross-platform compatible way.
23
24    Args:
25        cmd (List[str]): Command to run
26        cwd (PathLike): Working directory to run the command in
27        extra_env (Dict[str, str]): extra environment variables to define for the execution
28        **kwargs: optional arguments passed to `subprocess.run`
29
30    Returns:
31        CompletedProcess: If the execution succeeded
32        None: if there was a problem executing
33    """
34    subprocess_cwd = Path(os.getcwd() if cwd is None else cwd).resolve()
35    subprocess_env = None if extra_env is None else dict(os.environ, **extra_env)
36    subprocess_exe = shutil.which(cmd[0])
37
38    if subprocess_exe is None:
39        LOGGER.error("Cannot execute `%s`, is it installed and in PATH?", cmd[0])
40        return None
41
42    LOGGER.info(
43        "'%s' running (wd: %s)",
44        " ".join(cmd),
45        subprocess_cwd,
46    )
47
48    try:
49        return subprocess.run(
50            cmd,
51            executable=subprocess_exe,
52            cwd=subprocess_cwd,
53            env=subprocess_env,
54            check=True,
55            capture_output=True,
56            **kwargs,
57        )
58    except FileNotFoundError:
59        LOGGER.error("Could not execute `%s`, is it installed and in PATH?", cmd[0])
60    except subprocess.CalledProcessError as e:
61        LOGGER.error("'%s' returned non-zero exit code %d", cmd[0], e.returncode)
62        stdout, stderr = (
63            e.stdout.decode(errors="backslashreplace").strip(),
64            e.stderr.decode(errors="backslashreplace").strip(),
65        )
66        if stdout:
67            LOGGER.error("\nstdout: ".join(stdout.split("\n")))
68        if stderr:
69            LOGGER.error("\nstderr: ".join(stderr.split("\n")))
70    except OSError:
71        LOGGER.error("OS error executing:", exc_info=True)
72
73    return None

Execute a command in a cross-platform compatible way.

Args: cmd (List[str]): Command to run cwd (PathLike): Working directory to run the command in extra_env (Dict[str, str]): extra environment variables to define for the execution **kwargs: optional arguments passed to subprocess.run

Returns: CompletedProcess: If the execution succeeded None: if there was a problem executing