Coverage for archdocs/features/kubernetes/values.py: 100%

38 statements  

« prev     ^ index     » next       coverage.py v7.15.4, created at 2026-08-18 22:03 +0000

1import typing 

2 

3from archdocs.features.kubernetes import const 

4 

5 

6_COMMENT_PREFIX: typing.Final = "#" 

7_INLINE_COMMENT_PREFIX: typing.Final = " #" 

8_TEMPLATE_MARKER: typing.Final = "{{" 

9_LIST_ITEM_PREFIX: typing.Final = "-" 

10_LIST_ITEM_INDENT: typing.Final = 2 

11_QUOTE_CHARACTERS: typing.Final = "\"'" 

12_MEANINGLESS_VALUES: typing.Final = frozenset(("{}", "[]", "null", "~")) 

13 

14type _ParentKeys = list[tuple[int, str]] 

15 

16 

17def _remove_list_prefix(raw_indent: int, raw_content: str, /) -> tuple[int, str]: 

18 if not raw_content.startswith(_LIST_ITEM_PREFIX): 

19 return raw_indent, raw_content 

20 return raw_indent + _LIST_ITEM_INDENT, raw_content[len(_LIST_ITEM_PREFIX) :].strip() 

21 

22 

23def _make_one_value( 

24 line_indent: int, 

25 value_key: str, 

26 raw_value: str, 

27 parent_keys: _ParentKeys, 

28 /, 

29) -> const.ManifestValue | None: 

30 if not raw_value: 

31 parent_keys.append((line_indent, value_key)) 

32 return None 

33 if _TEMPLATE_MARKER in raw_value or raw_value in _MEANINGLESS_VALUES: 

34 return None 

35 return const.ManifestValue( 

36 value_path=(*(one_parent_key for _, one_parent_key in parent_keys), value_key), 

37 raw_value=raw_value, 

38 ) 

39 

40 

41def _read_one_pair(raw_indent: int, raw_content: str, parent_keys: _ParentKeys, /) -> const.ManifestValue | None: 

42 line_indent, line_content = _remove_list_prefix(raw_indent, raw_content) 

43 value_key, key_separator, raw_value = line_content.partition(":") 

44 if not key_separator: 

45 return None 

46 while parent_keys and parent_keys[-1][0] >= line_indent: 

47 parent_keys.pop() 

48 return _make_one_value( 

49 line_indent, 

50 value_key.strip(), 

51 raw_value.partition(_INLINE_COMMENT_PREFIX)[0].strip().strip(_QUOTE_CHARACTERS), 

52 parent_keys, 

53 ) 

54 

55 

56def _read_one_line(raw_line: str, parent_keys: _ParentKeys, /) -> const.ManifestValue | None: 

57 line_content: typing.Final = raw_line.strip() 

58 if not line_content or line_content.startswith((_COMMENT_PREFIX, _TEMPLATE_MARKER)): 

59 return None 

60 return _read_one_pair(len(raw_line) - len(raw_line.lstrip()), line_content, parent_keys) 

61 

62 

63def read_manifest_values(raw_source: str) -> const.ManifestValues: 

64 # Templated yaml is not yaml, so the nesting is restored from the indentation alone and every 

65 # `{{ ... }}` value is dropped: what a rendered chart would put there is nobody's guess here. 

66 parent_keys: typing.Final[_ParentKeys] = [] 

67 all_read_values: typing.Final = [ 

68 _read_one_line(one_raw_line, parent_keys) for one_raw_line in raw_source.split("\n") 

69 ] 

70 return tuple(one_value for one_value in all_read_values if one_value is not None)