Skip to content

Commit 06e477a

Browse files
authored
πŸš€ Release v8.0.0 (useblocks#1688)
1 parent 5b223fd commit 06e477a

11 files changed

Lines changed: 187 additions & 10 deletions

File tree

β€Ž.github/workflows/docker.yamlβ€Ž

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ on:
1616
paths: ['docker/**']
1717

1818
env:
19-
NEEDS_VERSION: 7.0.0
19+
NEEDS_VERSION: 8.0.0
2020
DEPLOY_IMAGE: ${{ github.event_name != 'pull_request' }}
2121

2222
jobs:

β€Ždocs/_static/tutorial_needs.jsonβ€Ž

Lines changed: 1 addition & 1 deletion
Large diffs are not rendered by default.

β€Ždocs/builders.rstβ€Ž

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ This is a JSON schema of for the data structure of a single need,
4848
and also includes a ``field_type`` for each field, to denote the source of the field,
4949
that can be one of: ``core``, ``links``, ``backlinks``, ``extra``, ``global``.
5050

51-
See also :ref:`needs_json_exclude_fields`, :ref:`needs_json_remove_defaults`, and :ref:`needs_reproducible_json` for more options on modifying the content of the ``needs.json`` file.
51+
See also :ref:`needs_json_exclude_fields`, :ref:`needs_json_remove_defaults`, :ref:`needs_reproducible_json`, and :ref:`needs_json_include_link_conditions` for more options on modifying the content of the ``needs.json`` file.
5252

5353
.. note:: ``needs_defaults_removed`` is a flag that is set to ``true`` if the defaults are removed from the needs. If it is missing or set to ``false``, the defaults are not removed.
5454

β€Ždocs/changelog.rstβ€Ž

Lines changed: 155 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,166 @@
44
Changelog
55
=========
66

7+
.. _`release:8.0.0`:
8+
9+
8.0.0
10+
-----
11+
12+
:Released: Unreleased
13+
:Full Changelog: `v7.0.0...v8.0.0 <https://github.com/useblocks/sphinx-needs/compare/7.0.0...5b223fd71eeb7402d5b60c1a5832434d8fd05e31>`__
14+
15+
This release introduces **conditional link assessment** β€” the ability to attach
16+
:ref:`filter_string` conditions to links that are checked against the target need at build time.
17+
It also overhauls the internal link representation and fixes ``links_from_content`` to
18+
use the parsed doctree instead of fragile regex matching.
19+
20+
Conditional link assessment
21+
...........................
22+
23+
A major motivation for requirements-management tooling is ensuring traceability β€”
24+
not just *that* two needs are linked, but that the link is **valid in context**.
25+
For example, a specification should only link to requirements that are in an
26+
``"open"`` or ``"approved"`` state, or a test should only reference an implementation
27+
at a compatible version.
28+
29+
Sphinx-Needs now supports this via **inline conditions** on link references
30+
(see :ref:`need_conditional_links`).
31+
Append a :ref:`filter_string` in square brackets after the target ID:
32+
33+
.. code-block:: rst
34+
35+
.. spec:: My Specification
36+
:links: REQ_001[status=="open"], REQ_002[version>=3]
37+
38+
Each condition is evaluated against the **target** need's fields.
39+
If a condition evaluates to ``False``, a ``needs.link_condition_failed`` warning is emitted;
40+
if the condition syntax is invalid, a ``needs.link_condition_invalid`` warning is emitted.
41+
Links without conditions continue to work exactly as before.
42+
43+
The condition is a standard :ref:`filter_string` expression, so you can use any Python-style
44+
comparison operators (``==``, ``!=``, ``>``, ``<``, ``>=``, ``<=``), membership checks
45+
(``in``, ``not in``), boolean connectives (``and``, ``or``, ``not``), and built-in helpers
46+
like ``search()``.
47+
48+
This means conditions already work with **numeric fields** such as ``integer`` or ``number``
49+
(decimal) types. For example, if you define a ``version`` field:
50+
51+
.. code-block:: toml
52+
53+
[needs.fields.version]
54+
schema.type = "integer"
55+
nullable = false
56+
57+
You can then enforce version constraints on links:
58+
59+
.. code-block:: rst
60+
61+
.. spec:: My Specification
62+
:links: REQ_001[version>=2]
63+
64+
When the condition contains square brackets (e.g. for list indexing), use multiple opening
65+
brackets β€” the parser matches N opening ``[`` with N closing ``]``:
66+
``REQ_001[[tags[0]=="important"]]``.
67+
68+
- ✨ Add conditional need link assessment (:pr:`1675`)
69+
- ♻️ Add ``_split_link_list`` parser with condition syntax support (:pr:`1674`)
70+
- πŸ‘Œ Parse link conditions from imported and external needs (:pr:`1680`)
71+
- πŸ‘Œ Add :ref:`needs_json_include_link_conditions` config option (:pr:`1681`)
72+
73+
Controls whether conditions are included in ``needs.json`` output (default: ``True``).
74+
Backlink fields are never affected.
75+
76+
- πŸ‘Œ Add ``parse_conditions`` configuration for link types (:pr:`1684`)
77+
78+
Per-link-type control over whether ``[condition]`` brackets are parsed.
79+
Set ``parse_conditions = false`` on a link type to treat brackets as literal ID text.
80+
81+
.. code-block:: toml
82+
83+
[needs.links.raw_links]
84+
parse_conditions = false
85+
86+
.. note::
87+
88+
**Potential future directions**:
89+
90+
- **Hash-based checks**: combined with a ``hash`` field and a dynamic function that
91+
computes a content hash, conditions like ``REQ_001[hash=="abc123"]`` could detect
92+
when a linked need's content has changed since the link was authored.
93+
- **Semantic version comparisons**: a dedicated semver field type would enable
94+
conditions like ``REQ_001[version~=">=1.2.0"]`` with proper semver semantics.
95+
- **Terse constraint syntax**: a more compact notation (e.g. ``REQ_001[s=open,v>=2]``
96+
with field shorthands) is under consideration for common cases, but would
97+
complement β€” not replace β€” the current filter-string syntax.
98+
99+
``links_from_content`` rewrite
100+
..............................
101+
102+
The ``links_from_content`` dynamic function previously used a regex to
103+
extract ``:need:`ID``` references from raw RST source text.
104+
This was fragile and could not handle custom titles (e.g. ``:need:`My Title <REQ_001>```),
105+
nested content, or other edge cases.
106+
107+
It now walks the **parsed doctree** for the source need, collecting ``NeedRef`` nodes
108+
directly. This is more robust and correctly handles all role syntax variants.
109+
110+
**Limitations**: ``links_from_content`` requires a stored doctree node.
111+
It will emit a warning and return an empty list for:
112+
113+
- **External needs** (loaded via ``needimport`` / ``needs_external_needs``) β€” they have no doctree.
114+
- **Need parts** β€” not supported; a warning is emitted.
115+
116+
- ♻️ Fix ``links_from_content`` to use parsed doctree nodes instead of regex (:pr:`1685`)
117+
118+
Breaking changes
119+
................
120+
121+
- ‼️ ``ENV_DATA_VERSION`` bumped to 4 (:pr:`1683`)
122+
123+
The internal format for storing link data in the Sphinx build environment has changed
124+
(links are now stored as structured ``NeedLink`` objects instead of plain strings).
125+
**Incremental builds from a previous version will trigger a full rebuild automatically.**
126+
127+
- ‼️ ``need["links"]`` (and other link fields) now returns a fresh ``list[str]`` copy
128+
rather than a reference to the internal storage (:pr:`1670`)
129+
130+
Previously, code like ``need["links"].append("NEW_ID")`` would mutate the internal list.
131+
This now **silently has no effect** β€” the returned list is a projection from the internal
132+
``NeedLink`` representation. This should only affect exotic use cases such as dynamic
133+
functions or custom extensions that mutate link lists via ``__getitem__`` access.
134+
Use the ``NeedItem`` API (e.g. ``get_links(as_str=False)``) for direct access to the
135+
internal ``NeedLink`` objects.
136+
137+
Internal changes
138+
................
139+
140+
These changes do not affect user-facing behaviour but improve link handling internals:
141+
142+
- ♻️ Introduce ``NeedLink`` structured internal representation for links (:pr:`1670`)
143+
- ♻️ Store ``NeedLink`` instead of ``str`` in ``LinksLiteralValue`` and ``LinksFunctionArray`` (:pr:`1673`)
144+
- πŸ”§ Use ``NeedLink`` directly in ``update_back_links`` function (:pr:`1672`)
145+
- πŸ”§ Store ``NeedPartData.backlinks`` as ``NeedLink`` instead of ``str`` (:pr:`1679`)
146+
- πŸ”§ Use ``get_links(as_str=False)`` in needextend to avoid round-trip serialization (:pr:`1678`)
147+
- ♻️ Store ``NeedLink`` on ``NeedRef`` node at parse time instead of re-parsing later (:pr:`1682`)
148+
- πŸ§ͺ Add tests for variants in links (:pr:`1669`)
149+
150+
Bug fixes
151+
.........
152+
153+
- πŸ› Fix linkcheck CI job warnings (:pr:`1667`)
154+
155+
Documentation
156+
.............
157+
158+
- πŸ“š Add sphinx-ai-index to Sphinx docs builder (:pr:`1671`)
159+
7160
.. _`release:7.0.0`:
8161

9162
7.0.0
10163
-----
11164

12165
:Released: 24.02.2026
13-
:Full Changelog: `v6.3.0...v7.0.0 <https://github.com/useblocks/sphinx-needs/compare/6.3.0...0aea09eda386880272205cbdc8a98fde3ff3566a>`__
166+
:Full Changelog: `v6.3.0...v7.0.0 <https://github.com/useblocks/sphinx-needs/compare/6.3.0...7.0.0>`__
14167

15168
This is a major release that consolidates field, link, and default configuration
16169
into a single, composable schema system β€” inspired by
@@ -264,7 +417,7 @@ Documentation fixes
264417
-----
265418

266419
:Released: 15.12.2025
267-
:Full Changelog: `v6.2.0...v6.3.0 <https://github.com/useblocks/sphinx-needs/compare/6.2.0...f567c1fafb4e1ba1a7dabb3bd6afc5f17ded84cd>`__
420+
:Full Changelog: `v6.2.0...v6.3.0 <https://github.com/useblocks/sphinx-needs/compare/6.2.0...6.3.0>`__
268421

269422
- ⬆️ Support Python 3.14 (:pr:`1598`)
270423
- ♻️ Remove ``typeguard`` dependency (:pr:`1597`)

β€Ždocs/configuration.rstβ€Ž

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -383,7 +383,7 @@ Each configured link can define:
383383
- ``parse_dynamic_functions``: If set to ``True``, the field will support :ref:`dynamic_functions`.
384384
Default: the value of :ref:`needs_parse_dynamic_functions` (``True``).
385385
- ``parse_conditions``: If set to ``False``, the ``[condition]`` bracket syntax will not be parsed for this link type.
386-
Default: ``True``.
386+
Default: ``True``. *New in version 8.0.0.*
387387
- ``incoming`` (optional): Incoming text, to use for incoming links. E.g. "is blocked by". Default: "<name> incoming".
388388
- ``outgoing`` (optional): Outgoing text, to use for outgoing links. E.g. "blocks". Default: "<name>".
389389
- ``copy`` (optional): True/False. If True, the links will be copied also to the common link-list (link type ``links``).
@@ -2833,7 +2833,7 @@ Each configured link should define:
28332833
- ``parse_dynamic_functions``: If set to ``True``, the field will support :ref:`dynamic_functions`.
28342834
Default: the value of :ref:`needs_parse_dynamic_functions` (``True``).
28352835
- ``parse_conditions``: If set to ``False``, the ``[condition]`` bracket syntax will not be parsed for this link type.
2836-
Default: ``True``.
2836+
Default: ``True``. *New in version 8.0.0.*
28372837
- ``incoming`` (optional): Incoming text, to use for incoming links. E.g. "is blocked by".
28382838
- ``outgoing`` (optional): Outgoing text, to use for outgoing links. E.g. "blocks".
28392839
- ``copy`` (optional): True/False. If True, the links will be copied also to the common link-list (link type ``links``).

β€Ždocs/directives/need.rstβ€Ž

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,11 @@ conditional links
133133

134134
.. versionadded:: 8.0.0
135135

136+
In requirements management, traceability is not just about *whether* two needs are linked,
137+
but whether the link is **valid in context**.
138+
For example, a specification should only link to requirements that are ``"open"`` or ``"approved"``,
139+
or a test should only reference an implementation at a compatible version.
140+
136141
You can attach a condition to any link by appending a :ref:`filter_string` in square brackets after the target ID.
137142
The condition is evaluated against the **targeted** need.
138143
If the condition evaluates to ``False``, a warning is emitted with the subcode ``needs.link_condition_failed``.
@@ -180,6 +185,10 @@ Links without conditions continue to work as before:
180185
``"links": ["REQ_001[status==\"open\"]"]``
181186
and the condition will be evaluated during the build just as it would for a directive-defined link.
182187

188+
.. seealso::
189+
190+
:ref:`needs_json_include_link_conditions` β€” controls whether conditions are included in ``needs.json`` output.
191+
183192
extra links
184193
+++++++++++
185194

β€Ždocs/filter.rstβ€Ž

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,7 @@ Filter string
105105

106106
The usage of a filter string is supported/required by:
107107

108+
* :ref:`Conditional links <need_conditional_links>` (to assert properties of a link target)
108109
* :ref:`need_count`
109110
* :ref:`needlist`
110111
* :ref:`needtable`

β€Ždocs/ubproject.tomlβ€Ž

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,13 @@ content = false
4242
parse_content = false
4343
content_required = false
4444

45+
[parse.extend_directives.need-core-fields]
46+
argument = false
47+
options = false
48+
content = false
49+
parse_content = false
50+
content_required = false
51+
4552
[parse.extend_directives.page-summary]
4653
argument = false
4754
options = false

β€Žsphinx_needs/__init__.pyβ€Ž

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
"""Sphinx needs extension for managing needs/requirements and specifications"""
22

3-
__version__ = "7.0.0"
3+
__version__ = "8.0.0"
44

55

66
def setup(app): # type: ignore[no-untyped-def]

β€Žsphinx_needs/functions/common.pyβ€Ž

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -430,7 +430,11 @@ def links_from_content(
430430
431431
Same links are only added once.
432432
433-
.. note::
433+
.. versionchanged:: 8.0.0
434+
435+
Previously used a regex on raw RST source text to extract ``:need:`` references.
436+
Now walks the parsed doctree, which correctly handles custom titles
437+
(e.g. ``:need:`My Title <REQ_001>```) and nested content.
434438
435439
This function requires the source need to have a stored doctree node.
436440
It will emit a warning and return an empty list for needs without a

0 commit comments

Comments
Β (0)