Skip to content

🐛 Sort need links and backlinks naturally in needs.json and HTML#1695

Open
ubmarco wants to merge 5 commits intomasterfrom
mh-natural-sort
Open

🐛 Sort need links and backlinks naturally in needs.json and HTML#1695
ubmarco wants to merge 5 commits intomasterfrom
mh-natural-sort

Conversation

@ubmarco
Copy link
Copy Markdown
Member

@ubmarco ubmarco commented Apr 29, 2026

Summary

Closes #1371.

Need link and backlink lists are now naturally sorted (e.g. REQ_2 < REQ_9 < REQ_10) so build outputs are reproducible regardless of need load order, including when needs_external_needs is configured. Sorting happens unconditionally — no needs_reproducible_json flag is required — and applies to both needs.json and HTML output.

This matches the behaviour @ubmarco confirmed was desired: sort by default, in both outputs, irrespective of the needs_reproducible_json setting (which still keeps its existing role of stripping timestamps).

Implementation

  • NeedItem.sort_links() sorts _links, _backlinks and per-part backlinks in place using a natural sort key (digit runs are compared as ints, with the result alternating str/int so mixed-type comparisons are well-defined).
  • resolve_links() calls sort_links() on every need at the end of the post-processing pass, so all downstream consumers — JSON serialization in needsfile.py and the HTML rendering of outgoing/incoming refs in roles/need_outgoing.py and roles/need_incoming.py — see sorted lists.

Tests

New tests/test_needs_sort.py is YAML-driven and snapshot-based, modelled on tests/schema/test_schema.py:

  • tests/fixtures/sort_links.yml declares 5 cases:
    • alphabetical_outgoing — basic alphabetical sort of outgoing links
    • natural_outgoing — natural sort with digits (proves REQ_10 lands after REQ_9)
    • mixed_alphanumeric — mixed letter+number IDs
    • backlinks_sorted — backlinks naturally sorted (SPEC_1, SPEC_2, SPEC_10)
    • custom_link_types — sort works for custom link types (implements, derives)
  • For each case the test reads needs.json, derives link-field names from the embedded needs_schema (field_type ∈ {links, backlinks}), and snapshots the link/backlink fields per need. It also reads index.html, extracts (source need, target need) ref pairs by matching title="<source>" on link anchors, and snapshots that mapping. Both snapshots clearly show the natural ordering.

Existing snapshots in 8 unrelated test files were updated for the new sorted order; all changes are mechanical reorderings within link lists.

Test plan

  • CI passes
  • pytest tests/test_needs_sort.py — 5 cases pass, 10 snapshots match
  • pytest tests/ — no link-related regressions

Changelog

The bug-fix entry is in a new Unreleased section above 8.0.0 (now marked released on 19.03.2026).

ubmarco added 4 commits April 29, 2026 15:21
Need link and backlink lists are now naturally sorted (e.g.
``REQ_2`` < ``REQ_9`` < ``REQ_10``) so build outputs are reproducible
regardless of need load order, including when ``needs_external_needs`` is
configured. Sorting happens unconditionally — no ``needs_reproducible_json``
flag is required — and applies to both ``needs.json`` and HTML output.

Implementation: ``NeedItem.sort_links()`` sorts ``_links``, ``_backlinks``,
and per-part backlinks in place using a natural sort key that splits digit
runs and compares them as ints. ``resolve_links()`` calls this on every
need at the end of the post-processing pass, so all downstream consumers
(JSON serialization, HTML rendering of outgoing/incoming refs) see sorted
lists.

Tests: new ``tests/test_needs_sort.py`` with YAML-driven parametric cases
covering alphabetical/natural/mixed sorting, backlinks, and custom link
types — checks both the JSON and rendered HTML against snapshots.

Closes #1371
Adds two fixture cases with identical RST content but opposite values for
``needs_reproducible_json``. Their snapshots end up byte-identical, which
documents that sorting happens regardless of the flag.
@ubmarco ubmarco requested a review from chrisjsewell April 29, 2026 13:47
@codecov
Copy link
Copy Markdown

codecov Bot commented Apr 29, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 89.32%. Comparing base (4e10030) to head (c35a387).
⚠️ Report is 277 commits behind head on master.

Additional details and impacted files
@@            Coverage Diff             @@
##           master    #1695      +/-   ##
==========================================
+ Coverage   86.87%   89.32%   +2.44%     
==========================================
  Files          56       72      +16     
  Lines        6532    10340    +3808     
==========================================
+ Hits         5675     9236    +3561     
- Misses        857     1104     +247     
Flag Coverage Δ
pytests 89.32% <100.00%> (+2.44%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@ubmarco
Copy link
Copy Markdown
Member Author

ubmarco commented Apr 29, 2026

This PR is missing a performance analysis. Sorting links in big projects can be expensive.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Need links are not sorted in needs.json

1 participant