Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

👌 Capture only expressions for each need #1112

Draft
wants to merge 1 commit into
base: master
Choose a base branch
from
Draft
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
5 changes: 5 additions & 0 deletions sphinx_needs/data.py
Original file line number Diff line number Diff line change
Expand Up @@ -200,6 +200,11 @@ class NeedsInfoType(NeedsBaseDataType):
query: str
url: str

only_expressions: list[str]
"""List of parent only expressions, from outer to inner.
Note this key is only present if there are any only expressions.
"""

# Note there are also:
# - dynamic default options that can be set by needs_extra_options config
# - dynamic global options that can be set by needs_global_options config
Expand Down
14 changes: 13 additions & 1 deletion sphinx_needs/directives/need.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
from docutils import nodes
from docutils.parsers.rst.states import RSTState, RSTStateMachine
from docutils.statemachine import StringList
from sphinx import addnodes
from sphinx.addnodes import desc_name, desc_signature
from sphinx.application import Sphinx
from sphinx.environment import BuildEnvironment
Expand Down Expand Up @@ -309,7 +310,8 @@ def purge_needs(app: Sphinx, env: BuildEnvironment, docname: str) -> None:

def analyse_need_locations(app: Sphinx, doctree: nodes.document) -> None:
"""Determine the location of each need in the doctree,
relative to its parent section(s) and need(s).
relative to its parent section(s) and need(s),
and also note any parent ``only`` expressions.

This data is added to the need's data stored in the Sphinx environment,
so that it can be used in tables and filters.
Expand Down Expand Up @@ -361,6 +363,16 @@ def analyse_need_locations(app: Sphinx, doctree: nodes.document) -> None:
need_info["parent_needs"] = parent_needs
need_info["parent_need"] = parent_needs[0]

# find any parent only expressions, and note them on the need data item
expressions = []
parent: nodes.Element = need_node
while parent := getattr(parent, "parent", None): # type: ignore
if isinstance(parent, addnodes.only): # noqa: SIM102
if expr := parent.get("expr"):
expressions.append(expr)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't we add a break here, so that we don't "look up" anymore after only was found?

Copy link
Member Author

@chrisjsewell chrisjsewell Feb 16, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

no; we want to capture ALL parent only expressions, for example (this is in the test case):

.. only:: not something

   .. only:: other

      .. req:: Requirement 4
         :id: REQ_4

here the requirement should only be kept if BOTH "not something" and "other" evaluate to true,
so we capture and store {"only_expressions": ["not something", "other"]}

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

good point and fully agree 👍

if expressions:
need_info["only_expressions"] = expressions[::-1]

if need_node.get("hidden"):
hidden_needs.append(need_node)

Expand Down
4 changes: 4 additions & 0 deletions tests/doc_test/doc_build_only/conf.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
version = "1"
extensions = ["sphinx_needs"]

needs_build_json = True
20 changes: 20 additions & 0 deletions tests/doc_test/doc_build_only/index.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
Title
=====

.. req:: Requirement 1
:id: REQ_1

.. only:: html

.. req:: Requirement 2
:id: REQ_2

.. req:: Requirement 3
:id: REQ_3

.. only:: not something

.. only:: other

.. req:: Requirement 4
:id: REQ_4
27 changes: 27 additions & 0 deletions tests/test_doc_build_only.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import json
from pathlib import Path

import pytest


@pytest.mark.parametrize(
"test_app",
[{"buildername": "html", "srcdir": "doc_test/doc_build_only", "no_plantuml": True}],
indirect=True,
)
def test_doc_build_only(test_app):
app = test_app

app.build()
assert app._warning.getvalue() == ""

needs = json.loads(Path(app.outdir, "needs.json").read_text("utf8"))
id_to_expr = {
k: v.get("only_expressions") for k, v in needs["versions"]["1"]["needs"].items()
}
assert id_to_expr == {
"REQ_1": None,
"REQ_2": ["html"],
"REQ_3": ["html"],
"REQ_4": ["not something", "other"],
}
Loading