Coverage for archdocs/features/kubernetes/values.py: 100%
43 statements
« prev ^ index » next coverage.py v7.15.3, created at 2026-08-03 00:00 +0000
« prev ^ index » next coverage.py v7.15.3, created at 2026-08-03 00:00 +0000
1import dataclasses
2import typing
5_COMMENT_PREFIX: typing.Final = "#"
6_INLINE_COMMENT_PREFIX: typing.Final = " #"
7_TEMPLATE_MARKER: typing.Final = "{{"
8_LIST_ITEM_PREFIX: typing.Final = "-"
9_LIST_ITEM_INDENT: typing.Final = 2
10_QUOTE_CHARACTERS: typing.Final = "\"'"
11_MEANINGLESS_VALUES: typing.Final = frozenset(("{}", "[]", "null", "~"))
14@typing.final
15@dataclasses.dataclass(slots=True, kw_only=True, frozen=True)
16class ManifestValue:
17 value_path: tuple[str, ...]
18 raw_value: str
21type ManifestValues = tuple[ManifestValue, ...]
22type ValuePath = tuple[str, ...]
23type ParentKeys = list[tuple[int, str]]
26def _remove_list_prefix(raw_indent: int, raw_content: str, /) -> tuple[int, str]:
27 if not raw_content.startswith(_LIST_ITEM_PREFIX):
28 return raw_indent, raw_content
29 return raw_indent + _LIST_ITEM_INDENT, raw_content[len(_LIST_ITEM_PREFIX) :].strip()
32def _make_one_value(
33 line_indent: int,
34 value_key: str,
35 raw_value: str,
36 parent_keys: ParentKeys,
37 /,
38) -> ManifestValue | None:
39 if not raw_value:
40 parent_keys.append((line_indent, value_key))
41 return None
42 if _TEMPLATE_MARKER in raw_value or raw_value in _MEANINGLESS_VALUES:
43 return None
44 return ManifestValue(
45 value_path=(*(one_parent_key for _, one_parent_key in parent_keys), value_key),
46 raw_value=raw_value,
47 )
50def _read_one_pair(raw_indent: int, raw_content: str, parent_keys: ParentKeys, /) -> ManifestValue | None:
51 line_indent, line_content = _remove_list_prefix(raw_indent, raw_content)
52 value_key, key_separator, raw_value = line_content.partition(":")
53 if not key_separator:
54 return None
55 while parent_keys and parent_keys[-1][0] >= line_indent:
56 parent_keys.pop()
57 return _make_one_value(
58 line_indent,
59 value_key.strip(),
60 raw_value.partition(_INLINE_COMMENT_PREFIX)[0].strip().strip(_QUOTE_CHARACTERS),
61 parent_keys,
62 )
65def _read_one_line(raw_line: str, parent_keys: ParentKeys, /) -> ManifestValue | None:
66 line_content: typing.Final = raw_line.strip()
67 if not line_content or line_content.startswith((_COMMENT_PREFIX, _TEMPLATE_MARKER)):
68 return None
69 return _read_one_pair(len(raw_line) - len(raw_line.lstrip()), line_content, parent_keys)
72def read_manifest_values(raw_source: str) -> ManifestValues:
73 # Templated yaml is not yaml, so the nesting is restored from the indentation alone and every
74 # `{{ ... }}` value is dropped: what a rendered chart would put there is nobody's guess here.
75 parent_keys: typing.Final[ParentKeys] = []
76 all_read_values: typing.Final = [
77 _read_one_line(one_raw_line, parent_keys) for one_raw_line in raw_source.split("\n")
78 ]
79 return tuple(one_value for one_value in all_read_values if one_value is not None)