diff --git a/docs/configuration.rst b/docs/configuration.rst index 251c164db..cad82de98 100644 --- a/docs/configuration.rst +++ b/docs/configuration.rst @@ -48,7 +48,7 @@ This would allow Sphinx to perform incremental builds which are much faster as c # conf.py # Defining one or more functions in conf.py is fine - def my_custom_warning(need, log): + def my_custom_warning(need, log, needs): # some checks return False @@ -1365,30 +1365,64 @@ This will handle **all warnings** as exceptions. .. code-block:: python + # conf.py + + # Defining one or more functions in conf.py is fine def my_custom_warning_check(need, log): if need["status"] == "open": log.info(f"{need['id']} status must not be 'open'.") return True return False + def custom_warning_multi_needs(need, log, needs): + # need is closed, but linked need is still open. + # This is not allowed by this rule. + return_code: bool = False + if need["status"] in [ + "closed", + "done", + ]: + for linked_need in need["depend"]: + if needs[linked_need]["status"] not in [ + "closed", + "done", + ]: + return_code = True + + return return_code needs_warnings = { - # req need must not have an empty status field - 'req_with_no_status': "type == 'req' and not status", + # req need must not have an empty status field + 'req_with_no_status': "type == 'req' and not status", - # status must be open or closed - 'invalid_status' : "status not in ['open', 'closed']", + # status must be open or closed + 'invalid_status' : "status not in ['open', 'closed']", - # user defined filter code function - 'type_match': my_custom_warning_check, + # This assignment will deactivate incremental build support inside Sphinx and + # can create an unpickable configuration warning. + # user defined filter code function + 'type_match': my_custom_warning_check, } + # Better, register all warnings via Sphinx-Needs API. In all cases avoid to double definition of filters. + from sphinx_needs.api.configuration import add_warning + def setup(app): + #add_warning(app, "req_with_no_status", "type == 'req' and not status" + #add_warning(app, "invalid_status", "status not in ['open', 'closed']" + #add_warning(app, "type_match", my_custom_warning_check + add_warning(app, "depend_need_not_closed", custom_warning_multi_needs) + ``needs_warnings`` must be a dictionary. The **dictionary key** is used as identifier and gets printed in log outputs. The **value** must be a valid filter string or a custom defined filter code function and defines a *not allowed behavior*. So use the filter string or filter code function to define how needs are not allowed to be configured/used. -The defined filter code function must return ``True`` or ``False``. +The defined filter string and filter code function must return: + +* ``True`` - Warning shall be raised +* ``False`` - No warning needed + + .. warning:: @@ -1399,22 +1433,27 @@ Example output: .. code-block:: text - ... - looking for now-outdated files... none found - pickling environment... done - checking consistency... WARNING: Sphinx-Needs warnings were raised. See console / log output for details. - - Checking Sphinx-Needs warnings - type_check: passed - invalid_status: failed - failed needs: 11 (STYLE_005, EX_ROW_1, EX_ROW_3, copy_2, clv_1, clv_2, clv_3, clv_4, clv_5, T_C3893, R_AD4A0) - used filter: status not in ["open", "in progress", "closed", "done"] and status is not None - - type_match: failed - failed needs: 1 (TC_001) - used filter: - done - ... + ... + looking for now-outdated files... none found + pickling environment... done + checking consistency... WARNING: Sphinx-Needs warnings were raised. See console / log output for details. + + Checking Sphinx-Needs warnings + type_check: passed + invalid_status: failed + failed needs: 11 (STYLE_005, EX_ROW_1, EX_ROW_3, copy_2, clv_1, clv_2, clv_3, clv_4, clv_5, T_C3893, R_AD4A0) + used filter: status not in ["open", "in progress", "closed", "done"] and status is not None + + type_match: failed + failed needs: 1 (TC_001) + used filter: + + depend_need_not_closed: failed + failed needs: 1 (TC_MULTI2) + used filter: + + done + ... Due to the nature of Sphinx logging, a sphinx-warning may be printed wherever in the log. diff --git a/sphinx_needs/warnings.py b/sphinx_needs/warnings.py index 5c0e78601..afc2a3881 100644 --- a/sphinx_needs/warnings.py +++ b/sphinx_needs/warnings.py @@ -5,6 +5,8 @@ from __future__ import annotations +from inspect import signature + from sphinx.application import Sphinx from sphinx.util import logging @@ -72,8 +74,13 @@ def process_warnings(app: Sphinx, exception: Exception | None) -> None: # custom defined filter code used from conf.py result = [] for need in needs_view.values(): - if warning_filter(need, logger): - result.append(need) + sig = signature(warning_filter) + if len(sig.parameters) >= 3: + if warning_filter(need, logger, needs_view): # type: ignore[call-arg] + result.append(need) + else: + if warning_filter(need, logger): + result.append(need) else: log_warning(logger, f"Unknown needs warnings filter {warning_filter}!") diff --git a/tests/doc_test/doc_needs_warnings/conf.py b/tests/doc_test/doc_needs_warnings/conf.py index 1856456a7..e240328a5 100644 --- a/tests/doc_test/doc_needs_warnings/conf.py +++ b/tests/doc_test/doc_needs_warnings/conf.py @@ -33,6 +33,14 @@ }, ] +needs_extra_links = [ + { + "option": "depend", + "incoming": "depended", + "outgoing": "depends on", + }, +] + needs_external_needs = [ { "base_url": "http://my_company.com/docs/v1/", @@ -59,6 +67,24 @@ def custom_warning_func(need, log): return need["status"] == "example_3" +def custom_warning_multi_needs(need, log, needs): + # need is closed, but linked need is still open. + # This is not allowed by this rule. + return_code: bool = False + if need["status"] in [ + "closed", + "done", + ]: + for linked_need in need["depend"]: + if needs[linked_need]["status"] not in [ + "closed", + "done", + ]: + return_code = True + + return return_code + + def setup(app): from sphinx_needs.api.configuration import add_warning @@ -69,6 +95,7 @@ def setup(app): "invalid_status", "status not in ['open', 'closed', 'done', 'example_2', 'example_3']", ) + add_warning(app, "depend_need_not_closed", custom_warning_multi_needs) # Needs option to set True or False to raise sphinx-warning for each not passed warning check diff --git a/tests/doc_test/doc_needs_warnings/index.rst b/tests/doc_test/doc_needs_warnings/index.rst index be6596232..1718f8977 100644 --- a/tests/doc_test/doc_needs_warnings/index.rst +++ b/tests/doc_test/doc_needs_warnings/index.rst @@ -27,3 +27,12 @@ TEST DOCUMENT NEEDS WARNINGS .. test:: Test example 3 :id: TC_003 :status: example_3 + +.. test:: Multi needs 1 + :id: TC_MULTI1 + :status: open + +.. test:: Multi needs 2 + :id: TC_MULTI2 + :status: closed + :depend: TC_MULTI1 diff --git a/tests/test_needs_warning.py b/tests/test_needs_warning.py index f30e2ec16..342d7f3ce 100644 --- a/tests/test_needs_warning.py +++ b/tests/test_needs_warning.py @@ -37,8 +37,11 @@ def test_needs_warnings(test_app): "WARNING: invalid_status: failed", "\t\tfailed needs: 2 (SP_TOO_001, US_63252)", "\t\tused filter: status not in ['open', 'closed', 'done', 'example_2', 'example_3'] [needs.warnings]", + "WARNING: depend_need_not_closed: failed", + "\t\tfailed needs: 1 (TC_MULTI2)", + "\t\tused filter: custom_warning_multi_needs [needs.warnings]", "WARNING: type_match: failed", - "\t\tfailed needs: 1 (TC_001)", + "\t\tfailed needs: 2 (TC_001, TC_MULTI1)", "\t\tused filter: my_custom_warning_check [needs.warnings]", ]