diff --git a/src/extensions/score_metamodel/__init__.py b/src/extensions/score_metamodel/__init__.py index 557e7b974..3dd0c7726 100644 --- a/src/extensions/score_metamodel/__init__.py +++ b/src/extensions/score_metamodel/__init__.py @@ -294,6 +294,7 @@ def postprocess_need_links(needs_types_list: list[ScoreNeedType]): def setup(app: Sphinx) -> dict[str, str | bool]: app.add_config_value("external_needs_source", "", rebuild="env") app.add_config_value("score_metamodel_yaml", "", rebuild="env") + app.add_config_value("unmutable_options", [], rebuild="env") config_setdefault(app.config, "needs_id_required", True) config_setdefault(app.config, "needs_id_regex", "^[A-Za-z0-9_-]{6,}") @@ -308,6 +309,7 @@ def setup(app: Sphinx) -> dict[str, str | bool]: app.config.needs_fields.update(metamodel.needs_fields) app.config.graph_checks = metamodel.needs_graph_check app.config.prohibited_words_checks = metamodel.prohibited_words_checks + app.config.unmutable_options = metamodel.unmutable_options # app.config.stop_words = metamodel["stop_words"] # app.config.weak_words = metamodel["weak_words"] diff --git a/src/extensions/score_metamodel/checks/check_options.py b/src/extensions/score_metamodel/checks/check_options.py index 1deec28e9..685c57a52 100644 --- a/src/extensions/score_metamodel/checks/check_options.py +++ b/src/extensions/score_metamodel/checks/check_options.py @@ -21,6 +21,7 @@ ) from score_metamodel.metamodel_types import AllowedLinksType from sphinx.application import Sphinx +from sphinx_needs.data import SphinxNeedsData from sphinx_needs.need_item import NeedItem @@ -291,3 +292,18 @@ def check_validity_consistency( f"valid_from ({valid_from}) >= valid_until ({valid_until})." ) log.warning_for_need(need, msg) + + +@local_check +def check_needextends_forbidden_options(app: Sphinx, need: NeedItem, log: CheckLogger): + extends_data = list(SphinxNeedsData(app.env).get_or_create_extends().values()) + dissallowed: list[str] = app.config.unmutable_options + for needsextends in extends_data: + location = f"{needsextends['docname']}:{needsextends['lineno']}" + modifications = needsextends["modifications"] + for option, _, _ in modifications: + if option in dissallowed: + log.warning( + f"Needextend in document: {needsextends['docname']} modifies {option} which is not allowed", + location, + ) diff --git a/src/extensions/score_metamodel/metamodel.yaml b/src/extensions/score_metamodel/metamodel.yaml index 7d82bde3b..bdbcc9a4f 100644 --- a/src/extensions/score_metamodel/metamodel.yaml +++ b/src/extensions/score_metamodel/metamodel.yaml @@ -53,6 +53,12 @@ prohibited_words_checks: - thing - absolutely +unmutable_options: + options: + - safety + - security + - status + needs_types: # See metamodel.md for how to define a new need type diff --git a/src/extensions/score_metamodel/yaml_parser.py b/src/extensions/score_metamodel/yaml_parser.py index 454a502c0..f17db9074 100644 --- a/src/extensions/score_metamodel/yaml_parser.py +++ b/src/extensions/score_metamodel/yaml_parser.py @@ -13,6 +13,7 @@ """Functionality related to reading in the SCORE metamodel.yaml""" from dataclasses import dataclass +from itertools import chain from pathlib import Path from typing import Any, cast @@ -32,6 +33,7 @@ class MetaModelData: needs_types: list[ScoreNeedType] needs_links: dict[str, dict[str, str]] needs_fields: dict[str, dict[str, Any]] + unmutable_options: list[str] prohibited_words_checks: list[ProhibitedWordCheck] needs_graph_check: dict[str, object] @@ -49,6 +51,10 @@ def _parse_prohibited_words( ] +def _parse_unmutable_options(option_dict: dict[str, list[str]]) -> list[str]: + return list(chain(*option_dict.values())) + + def default_options(): """ Helper function to get a list of all default options defined by @@ -216,6 +222,7 @@ def load_metamodel_data(yaml_path: Path | None = None) -> MetaModelData: needs_types=list(needs_types.values()), needs_links=_parse_links(data.get("needs_extra_links", {})), needs_fields=_collect_all_custom_options(needs_types), + unmutable_options=_parse_unmutable_options(data.get("unmutable_options", {})), prohibited_words_checks=prohibited_words_checks, needs_graph_check=data.get("graph_checks", {}), )