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

64 statements  

« prev     ^ index     » next       coverage.py v7.8.2, created at 2025-09-07 09:27 +0000

1from typing import List, Any, Dict, Tuple, 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 ) 

49 api.pluggable_object_parser( 

50 OPARSER_MANIFEST_ROOT, 

51 MK_MANIFEST_DEFINITIONS, 

52 object_parser_key=OPARSER_MANIFEST_DEFINITIONS, 

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

54 ) 

55 api.pluggable_manifest_rule( 

56 OPARSER_MANIFEST_DEFINITIONS, 

57 MK_MANIFEST_VARIABLES, 

58 ManifestVariablesParsedFormat, 

59 _handle_manifest_variables, 

60 source_format=Dict[str, str], 

61 ) 

62 api.pluggable_manifest_rule( 

63 OPARSER_MANIFEST_ROOT, 

64 MK_INSTALLATIONS, 

65 List[InstallRule], 

66 _handle_installation_rules, 

67 expected_debputy_integration_mode=not_integrations( 

68 INTEGRATION_MODE_DH_DEBPUTY_RRR 

69 ), 

70 ) 

71 api.pluggable_manifest_rule( 

72 OPARSER_MANIFEST_ROOT, 

73 MK_MANIFEST_REMOVE_DURING_CLEAN, 

74 List[RemoveDuringCleanParsedFormat], 

75 _handle_remove_during_clean, 

76 expected_debputy_integration_mode=only_integrations( 

77 INTEGRATION_MODE_FULL, 

78 ), 

79 source_format=List[FileSystemMatchRule], 

80 ) 

81 api.pluggable_object_parser( 

82 OPARSER_MANIFEST_ROOT, 

83 MK_PACKAGES, 

84 object_parser_key=OPARSER_PACKAGES, 

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

86 nested_in_package_context=True, 

87 ) 

88 register_build_system_rules(api) 

89 

90 

91class ManifestVersionFormat(DebputyParsedContent): 

92 manifest_version: ManifestVersion 

93 

94 

95class ListOfInstallRulesFormat(DebputyParsedContent): 

96 elements: List[InstallRule] 

97 

98 

99class DictFormat(DebputyParsedContent): 

100 mapping: Dict[str, Any] 

101 

102 

103class ManifestVariablesParsedFormat(DebputyParsedContent): 

104 variables: Dict[str, str] 

105 

106 

107class RemoveDuringCleanParsedFormat(DebputyParsedContent): 

108 pattern: FileSystemMatchRule 

109 

110 

111def _handle_version( 

112 _name: str, 

113 parsed_data: ManifestVersionFormat, 

114 _attribute_path: AttributePath, 

115 _parser_context: ParserContextData, 

116) -> str: 

117 manifest_version = parsed_data["manifest_version"] 

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

119 raise ManifestParseException( 

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

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

122 ) 

123 return manifest_version 

124 

125 

126def _handle_manifest_variables( 

127 _name: str, 

128 parsed_data: ManifestVariablesParsedFormat, 

129 variables_path: AttributePath, 

130 parser_context: ParserContextData, 

131) -> None: 

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

133 resolved_vars: Dict[str, Tuple[str, AttributePath]] = {} 

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

135 substitution = manifest_parser.substitution 

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

137 key_path = variables_path[key] 

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

139 raise ManifestParseException( 

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

141 " be used." 

142 ) 

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

144 raise ManifestParseException( 

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

146 f" {key_path.path_key_lc}." 

147 ) 

148 try: 

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

150 except DebputySubstitutionError: 

151 if not resolved_vars: 

152 raise 

153 # See if flushing the variables work 

154 substitution = manifest_parser.add_extra_substitution_variables( 

155 **resolved_vars 

156 ) 

157 resolved_vars = {} 

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

159 resolved_vars[key] = (value, key_path) 

160 substitution = manifest_parser.add_extra_substitution_variables(**resolved_vars) 

161 

162 

163def _handle_installation_rules( 

164 _name: str, 

165 parsed_data: List[InstallRule], 

166 _attribute_path: AttributePath, 

167 _parser_context: ParserContextData, 

168) -> List[InstallRule]: 

169 return parsed_data 

170 

171 

172def _handle_remove_during_clean( 

173 _name: str, 

174 parsed_data: List[RemoveDuringCleanParsedFormat], 

175 _attribute_path: AttributePath, 

176 _parser_context: ParserContextData, 

177) -> List[FileSystemMatchRule]: 

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

179 

180 

181def _handle_opaque_dict( 

182 _name: str, 

183 parsed_data: DictFormat, 

184 _attribute_path: AttributePath, 

185 _parser_context: ParserContextData, 

186) -> Dict[str, Any]: 

187 return parsed_data["mapping"]