diff --git a/sphinx_needs/functions/common.py b/sphinx_needs/functions/common.py index 73f4e3d2c..6079d7326 100644 --- a/sphinx_needs/functions/common.py +++ b/sphinx_needs/functions/common.py @@ -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 @@ -432,7 +435,9 @@ def links_from_content( This specification cares about the realisation of: * :need:`CON_REQ_1` + * :need:`My speed ` * :need:`My need ` + * :need:`My need secound time ` .. spec:: Test spec 2 :id: CON_SPEC_2 @@ -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) diff --git a/tests/doc_test/doc_df_links_from_content/conf.py b/tests/doc_test/doc_df_links_from_content/conf.py new file mode 100644 index 000000000..64873d961 --- /dev/null +++ b/tests/doc_test/doc_df_links_from_content/conf.py @@ -0,0 +1 @@ +extensions = ["sphinx_needs"] diff --git a/tests/doc_test/doc_df_links_from_content/index.rst b/tests/doc_test/doc_df_links_from_content/index.rst new file mode 100644 index 000000000..d4328cf13 --- /dev/null +++ b/tests/doc_test/doc_df_links_from_content/index.rst @@ -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 ` + - :need:`n2 ` + - :need:`n2.1 ` + - :need:`n2.1 ` + - :need:`n2.2 ` + - :need:`n2.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 diff --git a/tests/test_dynamic_functions.py b/tests/test_dynamic_functions.py index 65f2e9f28..4855feebf 100644 --- a/tests/test_dynamic_functions.py +++ b/tests/test_dynamic_functions.py @@ -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: N_REF_1,' + ' N_REF_1.1,' + ' N_REF_1.2,' + ' N_REF_2,' + ' N_REF_2.1,' + ' N_REF_2.2' + in html + ) + + @pytest.mark.parametrize( "test_app", [