summaryrefslogtreecommitdiff
path: root/mesonbuild/cargo/toml.py
blob: dda7cfda8f55dcc95ebdfedf3127e86beaf0a329 (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
from __future__ import annotations

import importlib
import shutil
import json
import typing as T

from ..mesonlib import MesonException, Popen_safe
if T.TYPE_CHECKING:
    from types import ModuleType


# tomllib is present in python 3.11, before that it is a pypi module called tomli,
# we try to import tomllib, then tomli,
tomllib: T.Optional[ModuleType] = None
toml2json: T.Optional[str] = None
for t in ['tomllib', 'tomli']:
    try:
        tomllib = importlib.import_module(t)
        break
    except ImportError:
        pass
else:
    # TODO: it would be better to use an Executable here, which could be looked
    # up in the cross file or provided by a wrap. However, that will have to be
    # passed in externally, since we don't have (and I don't think we should),
    # have access to the `Environment` for that in this module.
    toml2json = shutil.which('toml2json')

class TomlImplementationMissing(MesonException):
    pass


class CargoTomlError(MesonException):
    """Exception for TOML parsing errors, keeping proper location info."""


def load_toml(filename: str) -> T.Dict[str, object]:
    if tomllib:
        try:
            with open(filename, 'rb') as f:
                raw = tomllib.load(f)
        except tomllib.TOMLDecodeError as e:
            if hasattr(e, 'msg'):
                raise CargoTomlError(e.msg, file=filename, lineno=e.lineno, colno=e.colno) from e
            else:
                raise CargoTomlError(str(e), file=filename) from e
    else:
        if toml2json is None:
            raise TomlImplementationMissing('Could not find an implementation of tomllib, nor toml2json')

        p, out, err = Popen_safe([toml2json, filename])
        if p.returncode != 0:
            error_msg = err.strip() or 'toml2json failed to decode TOML'
            raise CargoTomlError(error_msg, file=filename)

        raw = json.loads(out)

    # tomllib.load() returns T.Dict[str, T.Any] but not other implementations.
    return T.cast('T.Dict[str, object]', raw)