summaryrefslogtreecommitdiff
path: root/src/setuptools_scm/utils.py
blob: 6233a3ad672ecff898fef637293c63f429564908 (plain)
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
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
"""
utils
"""
from __future__ import annotations

import logging
import subprocess
import textwrap
import warnings
from pathlib import Path
from types import CodeType
from types import FunctionType
from typing import NamedTuple
from typing import Sequence
from typing import TYPE_CHECKING

from . import _run_cmd

if TYPE_CHECKING:
    from . import _types as _t

log = logging.getLogger(__name__)


class _CmdResult(NamedTuple):
    out: str
    err: str
    returncode: int


def do_ex(cmd: _t.CMD_TYPE, cwd: _t.PathT = ".") -> _CmdResult:
    res = _run_cmd.run(cmd, cwd)
    return _CmdResult(res.stdout, res.stderr, res.returncode)


def do(cmd: _t.CMD_TYPE, cwd: _t.PathT = ".") -> str:
    out, err, ret = do_ex(cmd, cwd)
    if ret and log.getEffectiveLevel() > logging.DEBUG:
        print(err)
    return out


def data_from_mime(path: _t.PathT) -> dict[str, str]:
    content = Path(path).read_text(encoding="utf-8")
    log.debug("mime %s content:\n%s", path, textwrap.indent(content, "    "))
    # the complex conditions come from reading pseudo-mime-messages
    data = dict(x.split(": ", 1) for x in content.splitlines() if ": " in x)

    log.debug("mime %s data:\n%s", path, data)
    return data


def function_has_arg(fn: object | FunctionType, argname: str) -> bool:
    assert isinstance(fn, FunctionType)
    code: CodeType = fn.__code__
    return argname in code.co_varnames


def has_command(name: str, args: Sequence[str] = ["help"], warn: bool = True) -> bool:
    try:
        p = _run_cmd.run([name, *args], cwd=".", timeout=5)
    except OSError as e:
        log.exception("command %s missing: %s", name, e)
        res = False
    except subprocess.TimeoutExpired as e:
        log.warning("command %s timed out %s", name, e)
        res = False

    else:
        res = not p.returncode
    if not res and warn:
        warnings.warn("%r was not found" % name, category=RuntimeWarning)
    return res


def require_command(name: str) -> None:
    if not has_command(name, warn=False):
        raise OSError(f"{name!r} was not found")