Coverage for src/debputy/plugins/debputy/manifest_root_rules.py: 82%

64 statements  

« prev     ^ index     » next       coverage.py v7.8.2, created at 2026-01-16 17:20 +0000

1from typing import Any, TYPE_CHECKING, cast 

2 

3from debputy._manifest_constants import ( 

4 ManifestVersion, 

5 MK_MANIFEST_VERSION, 

6 MK_INSTALLATIONS, 

7 SUPPORTED_MANIFEST_VERSIONS, 

8 MK_MANIFEST_DEFINITIONS, 

9 MK_PACKAGES, 

10 MK_MANIFEST_VARIABLES, 

11 MK_MANIFEST_REMOVE_DURING_CLEAN, 

12) 

13from debputy.exceptions import DebputySubstitutionError 

14from debputy.installations import InstallRule 

15from debputy.manifest_parser.base_types import FileSystemMatchRule 

16from debputy.manifest_parser.exceptions import ManifestParseException 

17from debputy.manifest_parser.parser_data import ParserContextData 

18from debputy.manifest_parser.tagging_types import DebputyParsedContent 

19from debputy.manifest_parser.util import AttributePath 

20from debputy.plugin.api.impl import DebputyPluginInitializerProvider 

21from debputy.plugin.api.parser_tables import ( 

22 OPARSER_MANIFEST_ROOT, 

23 OPARSER_MANIFEST_DEFINITIONS, 

24 OPARSER_PACKAGES, 

25) 

26from debputy.plugin.api.spec import ( 

27 not_integrations, 

28 INTEGRATION_MODE_DH_DEBPUTY_RRR, 

29 INTEGRATION_MODE_FULL, 

30 only_integrations, 

31) 

32from debputy.plugins.debputy.build_system_rules import register_build_system_rules 

33from debputy.substitution import VariableNameState, SUBST_VAR_RE 

34 

35if TYPE_CHECKING: 

36 from debputy.highlevel_manifest_parser import YAMLManifestParser 

37 

38 

39def register_manifest_root_rules(api: DebputyPluginInitializerProvider) -> None: 

40 # Registration order matters. Notably, definitions must come before anything that can 

41 # use definitions (variables), which is why it is second only to the manifest version. 

42 api.pluggable_manifest_rule( 

43 OPARSER_MANIFEST_ROOT, 

44 MK_MANIFEST_VERSION, 

45 ManifestVersionFormat, 

46 _handle_version, 

47 source_format=ManifestVersion, 

48 register_value=False, 

49 ) 

50 api.pluggable_object_parser( 

51 OPARSER_MANIFEST_ROOT, 

52 MK_MANIFEST_DEFINITIONS, 

53 object_parser_key=OPARSER_MANIFEST_DEFINITIONS, 

54 on_end_parse_step=lambda _a, _b, _c, mp: mp._ensure_package_states_is_initialized(), 

55 ) 

56 api.pluggable_manifest_rule( 

57 OPARSER_MANIFEST_DEFINITIONS, 

58 MK_MANIFEST_VARIABLES, 

59 ManifestVariablesParsedFormat, 

60 _handle_manifest_variables, 

61 source_format=dict[str, str], 

62 register_value=False, 

63 ) 

64 api.pluggable_manifest_rule( 

65 OPARSER_MANIFEST_ROOT, 

66 MK_INSTALLATIONS, 

67 list[InstallRule], 

68 _handle_installation_rules, 

69 register_value=False, 

70 expected_debputy_integration_mode=not_integrations( 

71 INTEGRATION_MODE_DH_DEBPUTY_RRR 

72 ), 

73 ) 

74 api.pluggable_manifest_rule( 

75 OPARSER_MANIFEST_ROOT, 

76 MK_MANIFEST_REMOVE_DURING_CLEAN, 

77 list[RemoveDuringCleanParsedFormat], 

78 _handle_remove_during_clean, 

79 register_value=False, 

80 expected_debputy_integration_mode=only_integrations( 

81 INTEGRATION_MODE_FULL, 

82 ), 

83 source_format=list[FileSystemMatchRule], 

84 ) 

85 api.pluggable_object_parser( 

86 OPARSER_MANIFEST_ROOT, 

87 MK_PACKAGES, 

88 object_parser_key=OPARSER_PACKAGES, 

89 on_end_parse_step=lambda _a, _b, _c, mp: mp._ensure_package_states_is_initialized(), 

90 nested_in_package_context=True, 

91 ) 

92 register_build_system_rules(api) 

93 

94 

95class ManifestVersionFormat(DebputyParsedContent): 

96 manifest_version: ManifestVersion 

97 

98 

99class ListOfInstallRulesFormat(DebputyParsedContent): 

100 elements: list[InstallRule] 

101 

102 

103class DictFormat(DebputyParsedContent): 

104 mapping: dict[str, Any] 

105 

106 

107class ManifestVariablesParsedFormat(DebputyParsedContent): 

108 variables: dict[str, str] 

109 

110 

111class RemoveDuringCleanParsedFormat(DebputyParsedContent): 

112 pattern: FileSystemMatchRule 

113 

114 

115def _handle_version( 

116 _name: str, 

117 parsed_data: ManifestVersionFormat, 

118 _attribute_path: AttributePath, 

119 _parser_context: ParserContextData, 

120) -> str: 

121 manifest_version = parsed_data["manifest_version"] 

122 if manifest_version not in SUPPORTED_MANIFEST_VERSIONS: 122 ↛ 123line 122 didn't jump to line 123 because the condition on line 122 was never true

123 raise ManifestParseException( 

124 "Unsupported manifest-version. This implementation supports the following versions:" 

125 f' {", ".join(repr(v) for v in SUPPORTED_MANIFEST_VERSIONS)}"' 

126 ) 

127 return manifest_version 

128 

129 

130def _handle_manifest_variables( 

131 _name: str, 

132 parsed_data: ManifestVariablesParsedFormat, 

133 variables_path: AttributePath, 

134 parser_context: ParserContextData, 

135) -> None: 

136 variables = parsed_data.get("variables", {}) 

137 resolved_vars: dict[str, tuple[str, AttributePath]] = {} 

138 manifest_parser: "YAMLManifestParser" = cast("YAMLManifestParser", parser_context) 

139 substitution = manifest_parser.substitution 

140 for key, value_raw in variables.items(): 

141 key_path = variables_path[key] 

142 if not SUBST_VAR_RE.match("{{" + key + "}}"): 142 ↛ 143line 142 didn't jump to line 143 because the condition on line 142 was never true

143 raise ManifestParseException( 

144 f"The variable at {key_path.path_key_lc} has an invalid name and therefore cannot" 

145 " be used." 

146 ) 

147 if substitution.variable_state(key) != VariableNameState.UNDEFINED: 

148 raise ManifestParseException( 

149 f'The variable "{key}" is already reserved/defined. Error triggered by' 

150 f" {key_path.path_key_lc}." 

151 ) 

152 try: 

153 value = substitution.substitute(value_raw, key_path.path) 

154 except DebputySubstitutionError: 

155 if not resolved_vars: 

156 raise 

157 # See if flushing the variables work 

158 substitution = manifest_parser.add_extra_substitution_variables( 

159 **resolved_vars 

160 ) 

161 resolved_vars = {} 

162 value = substitution.substitute(value_raw, key_path.path) 

163 resolved_vars[key] = (value, key_path) 

164 substitution = manifest_parser.add_extra_substitution_variables(**resolved_vars) 

165 

166 

167def _handle_installation_rules( 

168 _name: str, 

169 parsed_data: list[InstallRule], 

170 _attribute_path: AttributePath, 

171 _parser_context: ParserContextData, 

172) -> list[InstallRule]: 

173 return parsed_data 

174 

175 

176def _handle_remove_during_clean( 

177 _name: str, 

178 parsed_data: list[RemoveDuringCleanParsedFormat], 

179 _attribute_path: AttributePath, 

180 _parser_context: ParserContextData, 

181) -> list[FileSystemMatchRule]: 

182 return [x["pattern"] for x in parsed_data] 

183 

184 

185def _handle_opaque_dict( 

186 _name: str, 

187 parsed_data: DictFormat, 

188 _attribute_path: AttributePath, 

189 _parser_context: ParserContextData, 

190) -> dict[str, Any]: 

191 return parsed_data["mapping"]