Skip to content

Commit cf51427

Browse files
committed
exclude needs under only directive if required (useblocks#1103)
1 parent 73b961e commit cf51427

File tree

5 files changed

+126
-0
lines changed

5 files changed

+126
-0
lines changed

.gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
.idea
2+
*.iml
23
.venv*
34
.pvenv
45
.nox

sphinx_needs/directives/need.py

+14
Original file line numberDiff line numberDiff line change
@@ -340,6 +340,8 @@ def analyse_need_locations(app: Sphinx, doctree: nodes.document) -> None:
340340
if need_node.get("hidden"):
341341
hidden_needs.append(need_node)
342342

343+
need_info["excluded_by_only"] = need_node_excluded_by_only_directive(env, need_node)
344+
343345
# now we have gathered all the information we need,
344346
# we can remove the hidden needs from the doctree
345347
for need_node in hidden_needs:
@@ -356,6 +358,18 @@ def previous_sibling(node: nodes.Node) -> Optional[nodes.Node]:
356358
return node.parent[i - 1] if i > 0 else None # type: ignore
357359

358360

361+
def need_node_excluded_by_only_directive(env: BuildEnvironment, node: nodes.Node) -> bool:
362+
"""Return True if the node is under an "only" directive that shall be excluded given to the current tags"""
363+
parent = node.parent
364+
while parent:
365+
if hasattr(parent, "tagname") and parent.tagname == "only":
366+
# note: we do not manage nested only directive
367+
only_tags = parent.attributes.get("expr", "")
368+
return env.app.builder.tags.eval_condition(only_tags)
369+
parent = parent.parent
370+
return False
371+
372+
359373
@profile("NEEDS_POST_PROCESS")
360374
@measure_time("need_post_process")
361375
def post_process_needs_data(app: Sphinx) -> None:
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
extensions = ["sphinx_needs"]
2+
3+
# also build the needs.json as some needs shall be hidden in it too
4+
needs_build_json = True
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
Only directive Test
2+
=====================
3+
4+
.. only:: tag_a
5+
6+
.. req:: req_001
7+
8+
I shall not appear if not running tag_a
9+
10+
.. only:: tag_b
11+
12+
.. req:: req_002
13+
14+
I shall not appear if not running tag_b
15+
16+
.. only:: tag_a or tag_b
17+
18+
.. req:: req_003
19+
20+
I shall not appear if not running either tag_a or tag_b
21+
22+
23+
.. req:: req_004
24+
25+
I shall always appear
26+
27+
28+
.. needtable::
29+
types: req
+78
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
import json
2+
from pathlib import Path
3+
4+
import pytest
5+
6+
7+
def get_json_needs(needs_file: Path):
8+
with open(needs_file, "r") as file:
9+
data = json.load(file)
10+
return data["versions"][data["current_version"]]["needs"]
11+
12+
@pytest.mark.parametrize(
13+
"test_app",
14+
[
15+
{"buildername": "html", "srcdir": "doc_test/doc_directive_only", "tags": ["tag_a"]},
16+
],
17+
indirect=True,
18+
)
19+
def test_need_excluded_under_only_a(test_app):
20+
app = test_app
21+
app.build()
22+
html = Path(app.outdir, "index.html").read_text()
23+
24+
assert "req_001" in html
25+
assert "req_002" not in html
26+
assert "req_003" in html
27+
assert "req_004" in html
28+
29+
needs_list = get_json_needs(Path(app.outdir, "needs.json"))
30+
31+
assert not needs_list["req_001"]["excluded_by_only"]
32+
assert needs_list["req_002"]["excluded_by_only"]
33+
assert not needs_list["req_003"]["excluded_by_only"]
34+
assert not needs_list["req_004"]["excluded_by_only"]
35+
36+
37+
@pytest.mark.parametrize(
38+
"test_app",
39+
[
40+
{"buildername": "html", "srcdir": "doc_test/doc_directive_only", "tags": ["tag_a"]},
41+
],
42+
indirect=True,
43+
)
44+
def test_need_excluded_under_only_b(test_app):
45+
app = test_app
46+
app.build()
47+
html = Path(app.outdir, "index.html").read_text()
48+
49+
assert "req_001" not in html
50+
assert "req_002" in html
51+
assert "req_003" in html
52+
assert "req_004" in html
53+
54+
needs_list = get_json_needs(Path(app.outdir, "needs.json"))
55+
56+
assert needs_list["req_001"]["excluded_by_only"]
57+
assert not needs_list["req_002"]["excluded_by_only"]
58+
assert not needs_list["req_003"]["excluded_by_only"]
59+
assert not needs_list["req_004"]["excluded_by_only"]
60+
61+
62+
@pytest.mark.parametrize("test_app", [{"buildername": "html", "srcdir": "doc_test/doc_directive_only"}], indirect=True)
63+
def test_need_excluded_under_only_no_tag(test_app):
64+
app = test_app
65+
app.build()
66+
html = Path(app.outdir, "index.html").read_text()
67+
68+
assert "req_001" not in html
69+
assert "req_002" not in html
70+
assert "req_003" not in html
71+
assert "req_004" in html
72+
73+
needs_list = get_json_needs(Path(app.outdir, "needs.json"))
74+
75+
assert needs_list["req_001"]["excluded_by_only"]
76+
assert needs_list["req_002"]["excluded_by_only"]
77+
assert needs_list["req_003"]["excluded_by_only"]
78+
assert not needs_list["req_004"]["excluded_by_only"]

0 commit comments

Comments
 (0)