-
Notifications
You must be signed in to change notification settings - Fork 1
/
toml-test-decoder
executable file
·113 lines (79 loc) · 2.75 KB
/
toml-test-decoder
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
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
#!/usr/bin/env python3
# SPDX-FileCopyrightText: 2024 Pablo Martinez Bernal (elpekenin)
#
# SPDX-License-Identifier: MIT
"""Utility used for testing against the TOML specification suite (toml-test)."""
# NOTE: We have a CLI-like interface and we dont care about PyLint preference for naming
# pylint: disable=invalid-name
from __future__ import annotations
import json
import sys
import warnings
from pathlib import Path
from typing import TypeAlias, TypedDict, TypeVar
K = TypeVar("K")
V = TypeVar("V")
TaggedVal: TypeAlias = (
"Tagged | list[Tagged] | dict[K, Tagged] | list[TaggedVal] | dict[K, TaggedVal]"
)
# this prints to stdout, which breaks toml-test
warnings.filterwarnings(action="ignore")
THIS = Path(__file__).parent
sys.path.insert(0, str(THIS))
# NOTE: We have to hack sys.path so that the library gets found when toml-test runs from
# anywhere, because of that we can't have this import at the very top of the file
import toml # noqa: E402
class Tagged(TypedDict):
"""A tagged value is type+value (in strings)."""
type: str
value: str
def type_name(val: object) -> str:
"""Convert Python's types into string, with a couple special cases."""
type_ = type(val)
return {
int: "integer",
str: "string",
}.get(type_, type_.__name__)
def val_repr(val: object) -> str:
"""Convert values to strings."""
# not needed?
if isinstance(val, str):
return val
return str(val)
def add_type(val: object) -> Tagged:
"""Represent the type and value of a variable in a string."""
return {
"type": type_name(val),
"value": val_repr(val),
}
def add_types_dict(data: dict[K, V]) -> dict[K, TaggedVal]:
"""Tag values in a dict, not the dict itself nor the keys."""
return {key: add_types(val) for key, val in data.items()}
def add_types_list(data: list[V]) -> list[TaggedVal]:
"""List themselves do not be tagged, their values do."""
return [add_types(val) for val in data]
def add_types(data: object) -> TaggedVal:
"""Add type tag to any kind of value."""
if isinstance(data, dict):
return add_types_dict(data)
if isinstance(data, list):
return add_types_list(data)
return add_type(data)
def main() -> int:
"""Entrypoint of the test suite.
Read from stdin and try to parse it.
On success, write type-tagged structure on stdout + exit(0)
On fail, optionally write to stderr + exit(1)
"""
try:
parsed = toml.load(sys.stdin)
except toml.TOMLError as exception:
sys.stderr.write(str(exception))
sys.stderr.write("\n")
return 1
typed = add_types_dict(parsed.data)
json.dump(typed, sys.stdout)
sys.stdout.write("\n")
return 0
if __name__ == "__main__":
sys.exit(main())