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)
|