forked from useblocks/sphinx-needs
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathneed_constraints.py
104 lines (86 loc) · 4.48 KB
/
need_constraints.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
from __future__ import annotations
import jinja2
from sphinx_needs.api.exceptions import NeedsConstraintFailed
from sphinx_needs.config import NeedsSphinxConfig
from sphinx_needs.data import NeedsMutable
from sphinx_needs.filter_common import filter_single_need
from sphinx_needs.logging import get_logger, log_warning
logger = get_logger(__name__)
def process_constraints(needs: NeedsMutable, config: NeedsSphinxConfig) -> None:
"""Analyse constraints of all needs,
and set corresponding fields on the need data item:
``constraints_passed`` and ``constraints_results``.
The ``style`` field may also be changed, if a constraint fails
(depending on the config value ``constraint_failed_options``)
"""
config_constraints = config.constraints
error_templates_cache: dict[str, jinja2.Template] = {}
for need in needs.values():
need_id = need["id"]
constraints = need["constraints"]
# flag that is set to False if any check fails
need["constraints_passed"] = True
for constraint in constraints:
try:
executable_constraints = config_constraints[constraint]
except KeyError:
# Note, this is already checked for in add_need
continue
# name is check_0, check_1, ...
for name, cmd in executable_constraints.items():
if name in ("severity", "error_message"):
# special keys, that are not a check
continue
# compile constraint and check if need fulfils it
constraint_passed = filter_single_need(need, config, cmd)
if constraint_passed:
need["constraints_results"].setdefault(constraint, {})[name] = True
else:
need["constraints_results"].setdefault(constraint, {})[name] = False
need["constraints_passed"] = False
if "error_message" in executable_constraints:
msg = str(executable_constraints["error_message"])
template = error_templates_cache.setdefault(
msg, jinja2.Template(msg)
)
need["constraints_error"] = template.render(**need)
if "severity" not in executable_constraints:
raise NeedsConstraintFailed(
f"'severity' key not set for constraint {constraint!r} in config 'needs_constraints'"
)
severity = executable_constraints["severity"]
if severity not in config.constraint_failed_options:
raise NeedsConstraintFailed(
f"Severity {severity!r} not set in config 'needs_constraint_failed_options'"
)
failed_options = config.constraint_failed_options[severity]
# log/except if needed
if "warn" in failed_options.get("on_fail", []):
log_warning(
logger,
f"Constraint {cmd} for need {need_id} FAILED! severity: {severity} {need.get('constraints_error', '')}",
"constraint",
location=(need["docname"], need["lineno"]),
color="red",
)
if "break" in failed_options.get("on_fail", []):
raise NeedsConstraintFailed(
f"FAILED a breaking constraint: >> {cmd} << for need "
f"{need_id} FAILED! breaking build process"
)
# set styles
old_style = need["style"]
if old_style and len(old_style) > 0:
new_styles = "".join(
", " + x for x in failed_options.get("style", [])
)
else:
old_style = ""
new_styles = "".join(
x + "," for x in failed_options.get("style", [])
)
if failed_options.get("force_style", False):
need["style"] = new_styles.strip(", ")
else:
constraint_failed_style = old_style + new_styles
need["style"] = constraint_failed_style