Coverage for src/debputy/plugin/debputy/manifest_root_rules.py: 80%
58 statements
« prev ^ index » next coverage.py v7.6.0, created at 2025-01-27 13:59 +0000
« prev ^ index » next coverage.py v7.6.0, created at 2025-01-27 13:59 +0000
1from typing import List, Any, Dict, Tuple, 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)
12from debputy.exceptions import DebputySubstitutionError
13from debputy.installations import InstallRule
14from debputy.manifest_parser.exceptions import ManifestParseException
15from debputy.manifest_parser.parser_data import ParserContextData
16from debputy.manifest_parser.tagging_types import DebputyParsedContent
17from debputy.manifest_parser.util import AttributePath
18from debputy.plugin.api.impl import DebputyPluginInitializerProvider
19from debputy.plugin.api.parser_tables import (
20 OPARSER_MANIFEST_ROOT,
21 OPARSER_MANIFEST_DEFINITIONS,
22 OPARSER_PACKAGES,
23)
24from debputy.plugin.api.spec import (
25 not_integrations,
26 INTEGRATION_MODE_DH_DEBPUTY_RRR,
27)
28from debputy.plugin.debputy.build_system_rules import register_build_system_rules
29from debputy.substitution import VariableNameState, SUBST_VAR_RE
31if TYPE_CHECKING:
32 from debputy.highlevel_manifest_parser import YAMLManifestParser
35def register_manifest_root_rules(api: DebputyPluginInitializerProvider) -> None:
36 # Registration order matters. Notably, definitions must come before anything that can
37 # use definitions (variables), which is why it is second only to the manifest version.
38 api.pluggable_manifest_rule(
39 OPARSER_MANIFEST_ROOT,
40 MK_MANIFEST_VERSION,
41 ManifestVersionFormat,
42 _handle_version,
43 source_format=ManifestVersion,
44 )
45 api.pluggable_object_parser(
46 OPARSER_MANIFEST_ROOT,
47 MK_MANIFEST_DEFINITIONS,
48 object_parser_key=OPARSER_MANIFEST_DEFINITIONS,
49 on_end_parse_step=lambda _a, _b, _c, mp: mp._ensure_package_states_is_initialized(),
50 )
51 api.pluggable_manifest_rule(
52 OPARSER_MANIFEST_DEFINITIONS,
53 MK_MANIFEST_VARIABLES,
54 ManifestVariablesParsedFormat,
55 _handle_manifest_variables,
56 source_format=Dict[str, str],
57 )
58 api.pluggable_manifest_rule(
59 OPARSER_MANIFEST_ROOT,
60 MK_INSTALLATIONS,
61 List[InstallRule],
62 _handle_installation_rules,
63 expected_debputy_integration_mode=not_integrations(
64 INTEGRATION_MODE_DH_DEBPUTY_RRR
65 ),
66 )
67 api.pluggable_object_parser(
68 OPARSER_MANIFEST_ROOT,
69 MK_PACKAGES,
70 object_parser_key=OPARSER_PACKAGES,
71 on_end_parse_step=lambda _a, _b, _c, mp: mp._ensure_package_states_is_initialized(),
72 nested_in_package_context=True,
73 )
75 register_build_system_rules(api)
78class ManifestVersionFormat(DebputyParsedContent):
79 manifest_version: ManifestVersion
82class ListOfInstallRulesFormat(DebputyParsedContent):
83 elements: List[InstallRule]
86class DictFormat(DebputyParsedContent):
87 mapping: Dict[str, Any]
90class ManifestVariablesParsedFormat(DebputyParsedContent):
91 variables: Dict[str, str]
94def _handle_version(
95 _name: str,
96 parsed_data: ManifestVersionFormat,
97 _attribute_path: AttributePath,
98 _parser_context: ParserContextData,
99) -> str:
100 manifest_version = parsed_data["manifest_version"]
101 if manifest_version not in SUPPORTED_MANIFEST_VERSIONS: 101 ↛ 102line 101 didn't jump to line 102 because the condition on line 101 was never true
102 raise ManifestParseException(
103 "Unsupported manifest-version. This implementation supports the following versions:"
104 f' {", ".join(repr(v) for v in SUPPORTED_MANIFEST_VERSIONS)}"'
105 )
106 return manifest_version
109def _handle_manifest_variables(
110 _name: str,
111 parsed_data: ManifestVariablesParsedFormat,
112 variables_path: AttributePath,
113 parser_context: ParserContextData,
114) -> None:
115 variables = parsed_data.get("variables", {})
116 resolved_vars: Dict[str, Tuple[str, AttributePath]] = {}
117 manifest_parser: "YAMLManifestParser" = cast("YAMLManifestParser", parser_context)
118 substitution = manifest_parser.substitution
119 for key, value_raw in variables.items():
120 key_path = variables_path[key]
121 if not SUBST_VAR_RE.match("{{" + key + "}}"): 121 ↛ 122line 121 didn't jump to line 122 because the condition on line 121 was never true
122 raise ManifestParseException(
123 f"The variable at {key_path.path_key_lc} has an invalid name and therefore cannot"
124 " be used."
125 )
126 if substitution.variable_state(key) != VariableNameState.UNDEFINED:
127 raise ManifestParseException(
128 f'The variable "{key}" is already reserved/defined. Error triggered by'
129 f" {key_path.path_key_lc}."
130 )
131 try:
132 value = substitution.substitute(value_raw, key_path.path)
133 except DebputySubstitutionError:
134 if not resolved_vars:
135 raise
136 # See if flushing the variables work
137 substitution = manifest_parser.add_extra_substitution_variables(
138 **resolved_vars
139 )
140 resolved_vars = {}
141 value = substitution.substitute(value_raw, key_path.path)
142 resolved_vars[key] = (value, key_path)
143 substitution = manifest_parser.add_extra_substitution_variables(**resolved_vars)
146def _handle_installation_rules(
147 _name: str,
148 parsed_data: List[InstallRule],
149 _attribute_path: AttributePath,
150 _parser_context: ParserContextData,
151) -> List[Any]:
152 return parsed_data
155def _handle_opaque_dict(
156 _name: str,
157 parsed_data: DictFormat,
158 _attribute_path: AttributePath,
159 _parser_context: ParserContextData,
160) -> Dict[str, Any]:
161 return parsed_data["mapping"]