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

1import dataclasses 

2import typing 

3 

4 

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", "~")) 

12 

13 

14@typing.final 

15@dataclasses.dataclass(slots=True, kw_only=True, frozen=True) 

16class ManifestValue: 

17 value_path: tuple[str, ...] 

18 raw_value: str 

19 

20 

21type ManifestValues = tuple[ManifestValue, ...] 

22type ValuePath = tuple[str, ...] 

23type ParentKeys = list[tuple[int, str]] 

24 

25 

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

30 

31 

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 ) 

48 

49 

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 ) 

63 

64 

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) 

70 

71 

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)