Add valid only allowed to link to valid check#539
Conversation
License Check Results🚀 The license check job ran with the Bazel command: bazel run --lockfile_mode=error //src:license-checkStatus: Click to expand output |
|
The created documentation from the pull request is available at: docu-html |
| ] | ||
| valid_needs_local = [ | ||
| x | ||
| for x in all_needs.filter_is_external(False).values() |
There was a problem hiding this comment.
Why filter out externals? Doesn't that mean it is ok to link invalid external needs?
There was a problem hiding this comment.
If you look at the check, what we want to do is we want to check that any LOCAL needs do not link to any invalid needs (local or external).
This way we will not check that external ones link badly.
| for need in valid_needs_local: | ||
| all_linked_needs: list[NeedLink] = list(chain(*need._links.values())) # type: ignore | ||
| for link in all_linked_needs: | ||
| if link.id not in valid_needs_id_all: |
There was a problem hiding this comment.
I'm a little worried about performance here because we will eventually have to process many needs items. Using lists this is O(n³). Just by using a set for valid_needs_id_all, this should be O(n²) which is bad enough.
There was a problem hiding this comment.
Not quiet sure I can follow the difference here.
Where would having a set instead of a list make a difference in this?
Yes lists isn't ideal already you are right, that's why I used the itertools chain to ensure we have the fastest way to group the links.
There was a problem hiding this comment.
With a list the not in check works by iterating over the list O(n). With a set, it is a hash-map-lookup O(1).
There was a problem hiding this comment.
Ohh, I did not know. I will adapt that for sure. I thought sets and lists are same lookup.
|
|
||
| for need in valid_needs_local: | ||
| all_linked_needs: list[NeedLink] = list(chain(*need._links.values())) # type: ignore | ||
| for link in all_linked_needs: |
There was a problem hiding this comment.
Can we check link.get("status") == "valid" instead of checking if the id is in some list?
There was a problem hiding this comment.
We could theoretically, but the issue is that the link we get here (NeedLink) is not a real Need.
We would then have to use that NeedLink.id to look up the actuall need in a dictionary.
So I thought it will be faster to gather all the 'valid needs' once, and then just check the id.
There was a problem hiding this comment.
Pull request overview
Adds a new graph check check_valid_only_links_to_valid that warns (as a non-fatal "new check" info) when a need with status: valid links to a need that is not valid. A companion RST-based test fixture is added but is intentionally not yet active.
Changes:
- New
@graph_checkfunction iterating all valid local needs and reporting links targeting non-valid needs vialog.warning_for_need(..., is_new_check=True). - Imports
chainfromitertoolsandNeedLinkfromsphinx_needs.need_item; reaches intoneed._linksto enumerate links. - New RST fixture
test_invalid_graph.rstdefining one valid child linking to an invalid parent.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 5 comments.
| File | Description |
|---|---|
| src/extensions/score_metamodel/checks/graph_checks.py | Implements the new check_valid_only_links_to_valid graph check and supporting imports. |
| src/extensions/score_metamodel/tests/rst/graph/test_invalid_graph.rst | New RST test fixture for the check; both #CHECK: and #EXPECT: are commented out, so it currently does not exercise the check. |
| :status: invalid | ||
|
|
||
| .. We can not yet enable this test. As the check is only an 'info' and not yet a true warning | ||
| .. #EXPECT: comp_saf_fmea__child__16: is valid but links to invalid need: |
| .. #CHECK: check_valid_only_links_to_valid | ||
|
|
||
| .. feat_req:: Parent requirement INVALID QM | ||
| :id: feat_req__parent__QM_invalid | ||
| :safety: QM | ||
| :status: invalid | ||
|
|
||
| .. We can not yet enable this test. As the check is only an 'info' and not yet a true warning | ||
| .. #EXPECT: comp_saf_fmea__child__16: is valid but links to invalid need: |
| valid_needs_id_all = [ | ||
| x.id for x in all_needs.values() if x.get("status") == "valid" | ||
| ] |
There was a problem hiding this comment.
Stolen valor :D
| ] | ||
|
|
||
| for need in valid_needs_local: | ||
| all_linked_needs: list[NeedLink] = list(chain(*need._links.values())) # type: ignore |
There was a problem hiding this comment.
There is no way around this that is better.
We already have all of the links inside the need element, I can access it like this easily.
Going around and doing it via the app.get_all_link_rypes and then CHECKING each link type if it's possible / doable in this need and valid, just seems very workaroundy and also much slower.
| # Get all possible link types | ||
| # Pre-Filter for only valid & local needs |
AlexanderLanin
left a comment
There was a problem hiding this comment.
AI-assisted review (Claude):
Critical: 0 | Important: 2 | Suggestions: 0
The check logic is sensible, but there are two issues that need attention before merge.
1. Test file has no assertions (Important)
test_invalid_graph.rst builds successfully but verifies nothing. The #EXPECT: line is inside an RST comment (..), so test_rules_file_based.py never parses it — the test framework matches line.startswith("#EXPECT:"), which is false for RST-commented lines. If the check function were deleted tomorrow, this test would still pass. The PR description calls this intentional (soft warning / is_new_check=True), but the result is a test file with zero test value. Consider either removing it until the check is promoted to a real warning, or adding a mechanism to verify INFO-level check output.
On top of this, the commented-out expected message references comp_saf_fmea__child__16, but the defined need has ID comp_saf_fmea__child__1 — so even if the line were uncommented, it would not match.
2. Dead links and invalid-status links conflated (Important)
If a valid need links to a non-existent ID (dead link), link.id not in valid_needs_id_all is also true, producing "is valid but links to invalid need: <dead-id>" when the linked need simply doesn't exist. This produces a misleading message and potentially duplicates what sphinx-needs already reports for dead links. The check should distinguish between a missing need and one with the wrong status.
On existing comments:
- The
listvssetperformance point raised by @a-zw is correct —valid_needs_id_allshould be asetfor O(1) lookup. An even cleaner fix is to drop the pre-collection entirely and resolve eachlink.iddirectly viaall_needs.get(link.id), which also handles the dead-link case above and avoids the_linksprivate attribute access.
Co-authored-by: Andreas Zwinkau <95761648+a-zw@users.noreply.github.com> Signed-off-by: Maximilian Sören Pollak <maximilian.pollak@qorix.com>
📌 Description
Add valid only allowed to link to valid check
The test is currently only a 'soft warning' and not enabled. (is_new_check =true).
In future releases this should be enabled and the RST based tests enbaled as well as the metamodel.yaml graph based tests adapted.
🚨 Impact Analysis
✅ Checklist