Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 11 additions & 7 deletions sphinx_needs/functions/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -416,12 +416,15 @@ def links_from_content(
All need-links set by using ``:need:`NEED_ID``` get extracted.

Same links are only added once.
need_part shall be supported.

Example:

.. req:: Requirement 1
:id: CON_REQ_1

* :np:`(speed) An acceleration of 200 m/s² or much much more`

.. req:: Requirement 2
:id: CON_REQ_2

Expand All @@ -432,7 +435,9 @@ def links_from_content(
This specification cares about the realisation of:

* :need:`CON_REQ_1`
* :need:`My speed <CON_REQ_1.speed>`
* :need:`My need <CON_REQ_2>`
* :need:`My need secound time <CON_REQ_2>`

.. spec:: Test spec 2
:id: CON_SPEC_2
Expand Down Expand Up @@ -466,13 +471,12 @@ def links_from_content(
if source_need is None:
raise ValueError("No need found for links_from_content")

links = re.findall(r":need:`(\w+)`|:need:`.+\<(.+)\>`", source_need["content"])
raw_links = []
for link in links:
if link[0] and link[0] not in raw_links:
raw_links.append(link[0])
elif link[1] and link[0] not in raw_links:
raw_links.append(link[1])
pattern = r":need:`(\w+(?:\.\w+)?)`|:need:`[^<]*<([^>]+)>`"
list_of_tuple = re.findall(pattern, source_need["content"])

links = [m[0] or m[1] for m in list_of_tuple]

raw_links = list(dict.fromkeys(links))

if filter:
needs_config = NeedsSphinxConfig(app.config)
Expand Down
1 change: 1 addition & 0 deletions tests/doc_test/doc_df_links_from_content/conf.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
extensions = ["sphinx_needs"]
43 changes: 43 additions & 0 deletions tests/doc_test/doc_df_links_from_content/index.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
DYNAMIC FUNCTION: links_from_content
====================================

.. need:: Reference Need 1
:id: N_REF_1

:np:`(1) sub 1`
:np:`(2) sub 1`

.. need:: Reference Need 2
:id: N_REF_2

:np:`(1) sub 1`
:np:`(2) sub 1`

.. need:: Main Need
:id: N_MAIN
:links: [[links_from_content()]]

This need links to

- :need:`N_REF_1`
- :need:`N_REF_1`
- :need:`N_REF_1.1`
- :need:`N_REF_1.1`
- :need:`N_REF_1.2`
- :need:`N_REF_1.2`
- :need:`n2 <N_REF_2>`
- :need:`n2 <N_REF_2>`
- :need:`n2.1 <N_REF_2.1>`
- :need:`n2.1 <N_REF_2.1>`
- :need:`n2.2 <N_REF_2.2>`
- :need:`n2.2 <N_REF_2.2>`

using dynamic function links_from_content.

Expected links:
- 1x N_REF_1
- 1x N_REF_1.1
- 1x N_REF_1.2
- 1x N_REF_2
- 1x N_REF_2.1
- 1x N_REF_2.2
28 changes: 28 additions & 0 deletions tests/test_dynamic_functions.py
Original file line number Diff line number Diff line change
Expand Up @@ -205,6 +205,34 @@ def test_doc_df_linked_values(test_app):
assert "all_awesome" in html


@pytest.mark.parametrize(
"test_app",
[
{
"buildername": "html",
"srcdir": "doc_test/doc_df_links_from_content",
"no_plantuml": True,
}
],
indirect=True,
)
def test_doc_df_links_from_content_values(test_app):
app = test_app
app.build()
warnings = strip_colors(app._warning.getvalue()).splitlines()
assert warnings == []
html = Path(app.outdir, "index.html").read_text()
assert (
'links outgoing: <span class="links"><span><a class="reference internal" href="#N_REF_1" title="N_MAIN">N_REF_1</a>,'
' <a class="reference internal" href="#N_REF_1.1" title="N_MAIN">N_REF_1.1</a>,'
' <a class="reference internal" href="#N_REF_1.2" title="N_MAIN">N_REF_1.2</a>,'
' <a class="reference internal" href="#N_REF_2" title="N_MAIN">N_REF_2</a>,'
' <a class="reference internal" href="#N_REF_2.1" title="N_MAIN">N_REF_2.1</a>,'
' <a class="reference internal" href="#N_REF_2.2" title="N_MAIN">N_REF_2.2</a>'
in html
)


@pytest.mark.parametrize(
"test_app",
[
Expand Down