Repository navigation
🐛 sphinx-needs: a read through computed links is a column read - #2143
Merged
Merged
Conversation
A link field's check_linked_values, or calc_sum with links_only, reads its need's own links. When a call computes those links in the same stratum, the order walked the list stored before the pass, which is empty for a computed list: a written target got no edge, so its back links were read as the pass had reset them and a link type it computes was read before it was (the run-time check's "please report this"), and an added target was read in order or not by the luck of its id. Which needs such links name is known only once they are computed, so the call now reads its field, and the fields its filter names, as a column, on every need: after every link field that computes it in stratum 1; out of scope (needs.derive_scope, the call not run) when it is computed after stratum 1 or is a back link or a dead-link flag; as written when no need computes it. A call in the links themselves stays a cycle. A cycle through such a column names the links. Ruled for both tools after ubCode's phase-1 review.
chrisjsewell
force-pushed
the
claude/magical-hopper-uw9j1q
branch
from
October 8, 2026 10:29
d2ca435 to
4476cf7
Compare
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## master #2143 +/- ##
==========================================
+ Coverage 92.82% 92.85% +0.02%
==========================================
Files 138 138
Lines 20313 20328 +15
==========================================
+ Hits 18856 18876 +20
+ Misses 1457 1452 -5
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
Package:
packages/sphinx-needs.This fixes
check_linked_valuesandcalc_sum(links_only=True)in a link field whose need's ownlinksare computed inthe same step. Until the links are computed, nobody knows which needs they name, including the ones written in them.
So the call now reads its field, and the fields its filter names, as a column, on every need. That is the same
column node a whole-project
calc_sumreads:What the call does now depends on where its field is computed:
refsabove): the call runs after every need that computesit, so it reads the computed value (above,
blocksis['T_THREE']once a linked need's computedrefsnamesT_ONE). It also still waits for its ownlinks.needs.derive_scopeand is not run, and the field keeps its written links. For example:dynamic function 'check_linked_values' for option 'blocks' reads 'links_back' on every need its links name, which is final only after the link fields are computed: the call is not run and the field is left empty.Two more cases:
linksthemselves still reads itself, which is a cycle.…, through its links, which are computed in the same step, so every need is a candidate.Code changes:
functions/order.py:_CallReads._linked()replaces the two walks of the stored list.OutOfScope.linkedrecords a back link or flag read on every need the links name.Reasoncan now be the reader's ownlinksnode.functions/functions.py: thederive_scopeandderive_cyclemessages for these reads.dynamic_functions.rst's processing order, a clause in the list of reads that cannot beordered, and a sentence on the cycle message. The changelog's Unreleased derived-values entry gets a clause and
cites this pull request.
Why
#2064 (derived values) and #2134 (phase 1, merged) order a call after every value it reads. #2136 was a follow-up.
ubCode's review of its own phase 1 found a gap in these reads. The order walked
need.get("links"), which is[]for a computed link list, so a written target got no edge. That had three effects:
check_linked_values(…, "links_back", …)passed silently.…the order of the pass did not account for this read; please report this….A target added by the computed list was read in order or not, depending on its id. That means the value depended on the
schedule, which the order exists to rule out.
The ruling holds for both tools: such a reader reads through a column. ubCode's phase 1 takes the same rule, with the
same wording for the back-link case.
Tests
tests/test_dynamic_functions_strata.py::test_a_read_through_computed_links_is_a_column_readuses reviewer A'sshapes (
LL_WBACK,LL_ABACK,LL_WLINK,LL_WLINK1,LL_ALINK, andZ_ALINKthree links down). It adds:LL_WFLT);LL_LATER);LL_FINAL,['T_ONE'], no finding);LL_SELF).It runs serially and with
-j 2, and asserts every value and every warning. The run-time check is silent on everyshape. Red before the change:
{'LL_WBACK': ['T_ONE']} != {'LL_WBACK': []}and{'LL_ABACK': ['T_ONE']} != {'LL_ABACK': []}(the call ran onthe reset back links);
{'LL_WFLT': []} != {'LL_WFLT': ['T_ONE']}(the filter readrefsbefore it was computed, silently).Before the change, its build also gave the "please report this" warning for
LL_WLINK,LL_WLINK1andLL_ALINK.tests/test_functions_order.py:(Column('refs'), ('RD', 'links'))for the option, alinks_onlysum anda filter; out of scope for a later field, a back link and a dead-link flag; final for a field no need computes.
The full suite (
uv run poe test-needs -n 4 tests/) passes, except the 50 graphviz renders that needdot(the same 50fail on master on a machine without it).
Checklist
Built in an orchestrated session from ubCode's review finding and the code. The tests were written first and shown
red, and every line was read.
uv run poe test-needs) and they pass (the graphviz renders excepted on a machinewithout
dot, identically to master).docs/dynamic_functions.rst).docs/changelog.rsthas the change in the existing Unreleased derived-values entry (its:pr:cite now names this number).
uv run poe lintanduv run poe typecheckpass.Refs #2064.
When squashing, strip the
Co-authored-bytrailer GitHub proposes.