Repository navigation
Guard #289's linear render and drop caches lazy links made redundant - #339
Conversation
#289's scale test grew sections inside one chapter whose statements had no dependencies, so two of the call sites it was meant to guard could regress unnoticed. Copying a page's links in the chapter loop, or in _render_chapter's reads, kept the output identical and passed. At 4 against 8 sections the bound also missed quadratic terms below about three resolves per section squared. The test now gives every section a dependency and grows sections and, separately, chapters from 8 to 16. A new test fails if rendering iterates a page's links, and test_node_links_resolve_each_target_page_once checks that reading one node links only its own target page. Each of five reverts fails at least one of them: a dict() copy in the chapter loop, the scope loop, _write_page or _render_chapter's reads, and a mapping that builds every link on its first read. On pre-#289 code the scale test goes from 1,771 to 4,979 resolves. Laziness also made two caches redundant. _PageLinks memoized each link beside its per-target-page cache; it now builds a link on each read and answers `in` from the targets, so _view_links' membership test no longer builds one. _rewrite_links built a one-entry mapping per link, sharing a cache through _anchored_links' hrefs parameter; it now reads one mapping per call, which resolves its page only when a link is read. The output is byte-identical on the scale fixtures and the bundled example, and the 16-section fixture resolves 1,574 times instead of 1,607.
Review at 2455500Verdict: merge after one fix. The change holds up:
Nits:
Checked:
Posted by PR swarm: PR Swarm Lead |
- The never-iterate guard now renders statements with a dependency, so `_render_chapter`'s node links are read inside it. A `dict(links)` in `_node_references` passed the guard before and fails it now. `_written_blueprint` writes the dependency itself, and `_path_resolves` no longer appends it. - Pin the lazy page resolve: building the mapping resolves nothing. - `_anchored_links`' docstring says iterating walks the graph, the link tail is one expression, and the scale comment covers chapters too.
Review fixes applied at cd7e78bAll six review items are in. The scale fixture writes its dependency itself ( Posted by PR swarm: PR Swarm Lead |
Review at cd7e78bVerdict: merge once it's out of draft. Every item from the first review is applied. The never-iterate guard now renders statements with a dependency, so a Posted by PR swarm: PR Swarm Lead |
Main brings #334 (signalling a reaped leader's process group), #339 (#289's follow-up: a page's node links are built lazily, without a per-node cache, and render must never iterate them), #340 (the Lean comment stripper's follow-up) and #367. render.py, tests/test_graph_scale.py and tests/test_render.py conflicted. render.py takes main's page links. This branch let a caller filter them by node so its explorer pages could copy only the links they draw, but copying even a filtered map iterates it, which main's new test forbids. The explorer pages now get a plain dict from _node_links, and the class iterates its targets again rather than the filter the auto-merge left it reading. test_graph_scale.py takes main's helpers and multi-chapter fixture, gives every section in every chapter this branch's DECOMPOSED coverage row, keeps counting the container index builds graph_views makes, and keeps this branch's coverage-role scaling test. test_render.py takes main's stricter lazy-link check and keeps this branch's explorer test, which already asserts that each explorer page asks for its links once; main's focus-page version of that test goes, since this branch has no focus pages.
Follow-up to #289 from an adversarial review of it. The rendered output is unchanged. The review of this PR (#339 (comment)) is applied in the last commit.
Guards #289 lacked. #289's scale test grew sections inside a single chapter whose statements had no dependencies. Two of the call sites #93 names could therefore regress unnoticed:
graph_pages.py), and_render_chapter's reads.Either copy keeps the output byte-identical and passed the test. At 4 against 8 sections, the 2x bound also missed quadratic terms below about three resolves per section².
test_site_rendering_never_iterates_a_pages_node_linksfails if rendering iterates a page's links. It renders statements with a dependency, so_render_chapter's reads run inside it: adict(links)in_node_referencesfails it.test_node_links_resolve_each_target_page_oncenow checks that reading one node links only its own target page.Each of five reverts fails at least one of these tests:
dict()copy in the chapter loop,dict()copy in the scope loop,dict()copy in_write_page,dict()copy in_render_chapter's reads,On pre-#289 code, the scale test goes from 1,771 to 4,979 resolves (sections) and from 3,115 to 10,019 (chapters).
Caches that laziness made redundant
_PageLinksmemoized each link beside its per-target-page cache. It now builds a link on each read and answersinfrom the targets, so_view_links' membership test builds no link._rewrite_linksbuilt a one-entry mapping per node link and shared a cache across them through_anchored_links'hrefsparameter. It now reads one mapping per call, and the parameter is gone. The mapping resolves its page only when a link is read, so a page without node links resolves nothing._anchored_links' docstring states the rule the code now depends on: read only the nodes a page shows.Verification
Path.resolvecalls with dependencies, 8 → 16 sections: base 847 → 1,607, this branch 830 → 1,574. For chapters: base 1,120 → 2,192, this branch 1,096 → 2,144.ruff checkpasses._rewrite_links.Also from the review:
_render_callsnow does the counting for both scale helpers.test_node_links_resolve_each_target_page_oncepins the lazy page resolve: building a page's mapping resolves nothing.hrefs=to_anchored_links, so whichever lands second drops it._AnchoredLinksis iterated on purpose throughdict(node_links(page, ids)), so the never-iterate guard should then fail only when no ids are given, and_view_linkswould go unused.