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
« prev ^ index » next coverage.py v7.8.2, created at 2026-01-16 17:20 +0000
1from typing import Any, TYPE_CHECKING, cast
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
35if TYPE_CHECKING:
36 from debputy.highlevel_manifest_parser import YAMLManifestParser
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)
95class ManifestVersionFormat(DebputyParsedContent):
96 manifest_version: ManifestVersion
99class ListOfInstallRulesFormat(DebputyParsedContent):
100 elements: list[InstallRule]
103class DictFormat(DebputyParsedContent):
104 mapping: dict[str, Any]
107class ManifestVariablesParsedFormat(DebputyParsedContent):
108 variables: dict[str, str]
111class RemoveDuringCleanParsedFormat(DebputyParsedContent):
112 pattern: FileSystemMatchRule
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
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)
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
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]
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"]