Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 0 additions & 2 deletions docs/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,5 @@
version = "0.1"

extensions = [
# TODO remove plantuml here once docs-as-code is updated to sphinx-needs 6
"sphinxcontrib.plantuml",
"score_sphinx_bundle",
]
7 changes: 4 additions & 3 deletions src/extensions/score_metamodel/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@

from sphinx.application import Sphinx
from sphinx_needs import logging
from sphinx_needs.data import NeedsInfoType, NeedsView, SphinxNeedsData
from sphinx_needs.data import NeedsView, SphinxNeedsData
from sphinx_needs.need_item import NeedItem

from src.extensions.score_metamodel.external_needs import connect_external_needs
from src.extensions.score_metamodel.log import CheckLogger
Expand All @@ -39,7 +40,7 @@

logger = logging.get_logger(__name__)

local_check_function = Callable[[Sphinx, NeedsInfoType, CheckLogger], None]
local_check_function = Callable[[Sphinx, NeedItem, CheckLogger], None]
graph_check_function = Callable[[Sphinx, NeedsView, CheckLogger], None]

local_checks: list[local_check_function] = []
Expand Down Expand Up @@ -170,7 +171,7 @@ def _remove_prefix(word: str, prefixes: list[str]) -> str:
return word


def _get_need_type_for_need(app: Sphinx, need: NeedsInfoType) -> ScoreNeedType:
def _get_need_type_for_need(app: Sphinx, need: NeedItem) -> ScoreNeedType:
for nt in app.config.needs_types:
if nt["directive"] == need["type"]:
return nt
Expand Down
10 changes: 5 additions & 5 deletions src/extensions/score_metamodel/checks/attributes_format.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@

from score_metamodel import CheckLogger, ProhibitedWordCheck, ScoreNeedType, local_check
from sphinx.application import Sphinx
from sphinx_needs.data import NeedsInfoType
from sphinx_needs.need_item import NeedItem


def get_need_type(needs_types: list[ScoreNeedType], directive: str) -> ScoreNeedType:
Expand All @@ -29,7 +29,7 @@ def get_need_type(needs_types: list[ScoreNeedType], directive: str) -> ScoreNeed

# req-Id: tool_req__docs_common_attr_id_scheme
@local_check
def check_id_format(app: Sphinx, need: NeedsInfoType, log: CheckLogger):
def check_id_format(app: Sphinx, need: NeedItem, log: CheckLogger):
"""
Checking if the title, directory and feature are included in
the requirement id or not.
Expand Down Expand Up @@ -57,7 +57,7 @@ def check_id_format(app: Sphinx, need: NeedsInfoType, log: CheckLogger):


@local_check
def check_id_length(app: Sphinx, need: NeedsInfoType, log: CheckLogger):
def check_id_length(app: Sphinx, need: NeedItem, log: CheckLogger):
"""
Validates that the requirement ID does not exceed the hard limit of 45 characters.
While the recommended limit is 30 characters, this check enforces a strict maximum
Expand Down Expand Up @@ -85,7 +85,7 @@ def check_id_length(app: Sphinx, need: NeedsInfoType, log: CheckLogger):


def _check_options_for_prohibited_words(
prohibited_word_checks: ProhibitedWordCheck, need: NeedsInfoType, log: CheckLogger
prohibited_word_checks: ProhibitedWordCheck, need: NeedItem, log: CheckLogger
):
options: list[str] = [
x for x in prohibited_word_checks.option_check if x != "types"
Expand All @@ -109,7 +109,7 @@ def _check_options_for_prohibited_words(
# req-Id: tool_req__docs_common_attr_desc_wording
# req-Id: tool_req__docs_common_attr_title
@local_check
def check_for_prohibited_words(app: Sphinx, need: NeedsInfoType, log: CheckLogger):
def check_for_prohibited_words(app: Sphinx, need: NeedItem, log: CheckLogger):
need_options = get_need_type(app.config.needs_types, need["type"])
prohibited_word_checks: list[ProhibitedWordCheck] = (
app.config.prohibited_words_checks
Expand Down
31 changes: 18 additions & 13 deletions src/extensions/score_metamodel/checks/check_options.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
# SPDX-License-Identifier: Apache-2.0
# *******************************************************************************
import re
from typing import cast

from score_metamodel import (
CheckLogger,
Expand All @@ -20,7 +21,7 @@
)
from score_metamodel.metamodel_types import AllowedLinksType
from sphinx.application import Sphinx
from sphinx_needs.data import NeedsInfoType
from sphinx_needs.need_item import NeedItem


def get_need_type(needs_types: list[ScoreNeedType], directive: str) -> ScoreNeedType:
Expand All @@ -31,9 +32,7 @@ def get_need_type(needs_types: list[ScoreNeedType], directive: str) -> ScoreNeed
raise ValueError(f"Need type {directive} not found in needs_types")


def _get_normalized(
need: NeedsInfoType, key: str, remove_prefix: bool = False
) -> list[str]:
def _get_normalized(need: NeedItem, key: str, remove_prefix: bool = False) -> list[str]:
"""Normalize a raw value into a list of strings."""
raw_value = need.get(key, None)
if not raw_value:
Expand All @@ -42,17 +41,23 @@ def _get_normalized(
if remove_prefix:
return [_remove_namespace_prefix_(raw_value)]
return [raw_value]
if isinstance(raw_value, list) and all(isinstance(v, str) for v in raw_value):
if isinstance(raw_value, list):
# Verify all elements are strings
raw_list = cast(list[object], raw_value)
for item in raw_list:
if not isinstance(item, str):
raise ValueError
str_list = cast(list[str], raw_value)
if remove_prefix:
return [_remove_namespace_prefix_(v) for v in raw_value]
return raw_value
return [_remove_namespace_prefix_(v) for v in str_list]
return str_list
raise ValueError


def _validate_value_pattern(
value: str,
pattern: str,
need: NeedsInfoType,
need: NeedItem,
field: str,
):
"""Check if a value matches the given pattern and log the result.
Expand All @@ -76,7 +81,7 @@ def _remove_namespace_prefix_(word: str) -> str:
def validate_options(
log: CheckLogger,
need_type: ScoreNeedType,
need: NeedsInfoType,
need: NeedItem,
):
"""
Validates that options in a need match their expected patterns.
Expand All @@ -103,7 +108,7 @@ def _validate(attributes_to_allowed_values: dict[str, str], mandatory: bool):
def validate_links(
log: CheckLogger,
need_type: ScoreNeedType,
need: NeedsInfoType,
need: NeedItem,
):
"""
Validates that links in a need match the expected types or regexes.
Expand Down Expand Up @@ -156,7 +161,7 @@ def _validate(
@local_check
def check_options(
app: Sphinx,
need: NeedsInfoType,
need: NeedItem,
log: CheckLogger,
):
"""
Expand All @@ -172,7 +177,7 @@ def check_options(
@local_check
def check_extra_options(
app: Sphinx,
need: NeedsInfoType,
need: NeedItem,
log: CheckLogger,
):
"""
Expand Down Expand Up @@ -224,7 +229,7 @@ def parse_milestone(value: str) -> tuple[int, int, int]:
@local_check
def check_validity_consistency(
app: Sphinx,
need: NeedsInfoType,
need: NeedItem,
log: CheckLogger,
):
"""
Expand Down
13 changes: 7 additions & 6 deletions src/extensions/score_metamodel/checks/graph_checks.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,11 @@
)
from sphinx.application import Sphinx
from sphinx_needs.config import NeedType
from sphinx_needs.data import NeedsInfoType, NeedsView
from sphinx_needs.data import NeedsView
from sphinx_needs.need_item import NeedItem


def eval_need_check(need: NeedsInfoType, check: str, log: CheckLogger) -> bool:
def eval_need_check(need: NeedItem, check: str, log: CheckLogger) -> bool:
"""
Perform a single check on a need:
1. Split the check into its parts
Expand Down Expand Up @@ -57,7 +58,7 @@ def eval_need_check(need: NeedsInfoType, check: str, log: CheckLogger) -> bool:


def eval_need_condition(
need: NeedsInfoType, condition: str | dict[str, list[Any]], log: CheckLogger
need: NeedItem, condition: str | dict[str, list[Any]], log: CheckLogger
) -> bool:
"""Evaluate a condition on a need:
1. Check if the condition is only a simple check (e.g. "status == valid")
Expand Down Expand Up @@ -101,16 +102,16 @@ def eval_need_condition(

def filter_needs_by_criteria(
needs_types: list[NeedType],
needs: list[NeedsInfoType],
needs: list[NeedItem],
needs_selection_criteria: dict[str, str],
log: CheckLogger,
) -> list[NeedsInfoType]:
) -> list[NeedItem]:
"""Create a list of needs that match the selection criteria.:
- If it is an include selection add the include to the pattern
- If it is an exclude selection add a "^" to the pattern
"""

selected_needs: list[NeedsInfoType] = []
selected_needs: list[NeedItem] = []
pattern: list[str] = []
need_pattern: str = list(needs_selection_criteria.keys())[0]
# Verify Inputs
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,11 @@
local_check,
)
from sphinx.application import Sphinx
from sphinx_needs.data import NeedsInfoType
from sphinx_needs.need_item import NeedItem


@local_check
def id_contains_feature(app: Sphinx, need: NeedsInfoType, log: CheckLogger):
def id_contains_feature(app: Sphinx, need: NeedItem, log: CheckLogger):
"""
The ID is expected to be in the format '<Req Type>__<feature>__<Title>'.
Most of this is ensured via regex in the metamodel.
Expand Down
24 changes: 12 additions & 12 deletions src/extensions/score_metamodel/checks/standards.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,15 +12,15 @@
# *******************************************************************************
# from sphinx.application import Sphinx

from sphinx_needs.data import NeedsInfoType
from sphinx_needs.need_item import NeedItem

# from score_metamodel import (
# CheckLogger,
# graph_check,
# )


def get_standards_needs(needs: list[NeedsInfoType]) -> dict[str, NeedsInfoType]:
def get_standards_needs(needs: list[NeedItem]) -> dict[str, NeedItem]:
"""
Return a dictionary of all standard requirements from the Sphinx app's needs.
"""
Expand All @@ -29,32 +29,32 @@ def get_standards_needs(needs: list[NeedsInfoType]) -> dict[str, NeedsInfoType]:


def get_standards_workproducts(
needs: list[NeedsInfoType],
) -> dict[str, NeedsInfoType]:
needs: list[NeedItem],
) -> dict[str, NeedItem]:
"""
Return a dictionary of standard workproducts from the Sphinx app's needs.
"""

return {need["id"]: need for need in needs if need["type"] == "std_wp"}


def get_workflows(needs: list[NeedsInfoType]) -> dict[str, NeedsInfoType]:
def get_workflows(needs: list[NeedItem]) -> dict[str, NeedItem]:
"""
Return a dictionary of all workflows from the Sphinx app's needs.
"""

return {need["id"]: need for need in needs if need.get("type") == "workflow"}


def get_workproducts(needs: list[NeedsInfoType]) -> dict[str, NeedsInfoType]:
def get_workproducts(needs: list[NeedItem]) -> dict[str, NeedItem]:
"""
Return a dictionary of all workproducts from the Sphinx app's needs.
"""

return {need["id"]: need for need in needs if need.get("type") == "workproduct"}


def get_compliance_req_needs(needs: list[NeedsInfoType]) -> set[str]:
def get_compliance_req_needs(needs: list[NeedItem]) -> set[str]:
"""
Return a set of all compliance_req values from the Sphinx app's needs,
but only if the need type is one of the specified process-related types.
Expand All @@ -68,7 +68,7 @@ def get_compliance_req_needs(needs: list[NeedsInfoType]) -> set[str]:
}


def get_compliance_wp_needs(needs: list[NeedsInfoType]) -> set[str]:
def get_compliance_wp_needs(needs: list[NeedItem]) -> set[str]:
"""
Return a set of all compliance_wp values from the Sphinx app's needs,
but only if the need type is "workproduct".
Expand Down Expand Up @@ -177,7 +177,7 @@ def get_compliance_wp_needs(needs: list[NeedsInfoType]) -> set[str]:


def my_pie_linked_standard_requirements(
needs: list[NeedsInfoType], results: list[int], **kwargs: str | int | float
needs: list[NeedItem], results: list[int], **kwargs: str | int | float
) -> None:
"""
Function to render the chart of check for standard requirements linked
Expand Down Expand Up @@ -210,7 +210,7 @@ def my_pie_linked_standard_requirements(


def my_pie_linked_standard_requirements_by_tag(
needs: list[NeedsInfoType], results: list[int], **kwargs: str | int | float
needs: list[NeedItem], results: list[int], **kwargs: str | int | float
) -> None:
"""
Filter function used for 'needpie' directives.
Expand Down Expand Up @@ -258,7 +258,7 @@ def my_pie_linked_standard_requirements_by_tag(


def my_pie_linked_standard_workproducts(
needs: list[NeedsInfoType], results: list[int], **kwargs: str | int | float
needs: list[NeedItem], results: list[int], **kwargs: str | int | float
) -> None:
"""
Function to render the chart of check for standar workproducts linked
Expand Down Expand Up @@ -292,7 +292,7 @@ def my_pie_linked_standard_workproducts(


def my_pie_workproducts_contained_in_exactly_one_workflow(
needs: list[NeedsInfoType], results: list[int], **kwargs: str | int | float
needs: list[NeedItem], results: list[int], **kwargs: str | int | float
) -> None:
"""
Function to render the chart of check for workproducts that are contained
Expand Down
12 changes: 5 additions & 7 deletions src/extensions/score_metamodel/log.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@

from docutils.nodes import Node
from sphinx_needs import logging
from sphinx_needs.data import NeedsInfoType
from sphinx_needs.logging import SphinxLoggerAdapter
from sphinx_needs.need_item import NeedItem

Location = str | tuple[str | None, int | None] | Node | None
NewCheck = tuple[str, Location]
Expand All @@ -32,7 +32,7 @@ def __init__(self, log: SphinxLoggerAdapter, prefix: str):
self._new_checks: list[NewCheck] = []

@staticmethod
def _location(need: NeedsInfoType, prefix: str):
def _location(need: NeedItem, prefix: str):
def get(key: str) -> Any:
return need.get(key, None)

Expand All @@ -49,15 +49,15 @@ def get(key: str) -> Any:
return None

def warning_for_option(
self, need: NeedsInfoType, option: str, msg: str, is_new_check: bool = False
self, need: NeedItem, option: str, msg: str, is_new_check: bool = False
):
full_msg = f"{need['id']}.{option} ({need.get(option, None)}): {msg}"
location = CheckLogger._location(need, self._prefix)
self._log_message(full_msg, location, is_new_check)

def warning_for_link(
self,
need: NeedsInfoType,
need: NeedItem,
option: str,
problematic_value: str,
allowed_values: list[str],
Expand All @@ -75,9 +75,7 @@ def warning_for_link(

self.warning_for_need(need, msg, is_new_check=is_new_check)

def warning_for_need(
self, need: NeedsInfoType, msg: str, is_new_check: bool = False
):
def warning_for_need(self, need: NeedItem, msg: str, is_new_check: bool = False):
full_msg = f"{need['id']}: {msg}"
location = CheckLogger._location(need, self._prefix)
self._log_message(full_msg, location, is_new_check)
Expand Down
Loading
Loading