From fa0ded52e491a28d6d97903bbf6761e7db06f585 Mon Sep 17 00:00:00 2001 From: Chris Sewell Date: Sun, 26 Jan 2025 14:28:01 +0100 Subject: [PATCH 1/3] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20Don't=20process=20dyna?= =?UTF-8?q?mic=20functions=20on=20internal=20need=20fields?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit For most "internal" need fields it does not make sense that these would be dynamic, and anyway this would fail since they are not string types. In this PR, we skip dynamic function processing, for core fields that should not be altered by the user. --- sphinx_needs/data.py | 13 ++++++++++ sphinx_needs/functions/functions.py | 38 +++++++++++++++-------------- 2 files changed, 33 insertions(+), 18 deletions(-) diff --git a/sphinx_needs/data.py b/sphinx_needs/data.py index 2cbc74d92..304687a61 100644 --- a/sphinx_needs/data.py +++ b/sphinx_needs/data.py @@ -68,6 +68,8 @@ class CoreFieldParameters(TypedDict): """Whether field should be excluded when importing needs (False if not present).""" exclude_json: NotRequired[bool] """Whether field should be part of the default exclusions from the JSON representation (False if not present).""" + allow_df: NotRequired[bool] + """Whether dynamic functions are allowed for this field (False if not present).""" NeedsCoreFields: Final[Mapping[str, CoreFieldParameters]] = { @@ -94,20 +96,24 @@ class CoreFieldParameters(TypedDict): "full_title": { "description": "Title of the need, of unlimited length.", "schema": {"type": "string", "default": ""}, + "allow_df": True, }, "title": { "description": "Title of the need, trimmed to a maximum length.", "schema": {"type": "string"}, + "allow_df": True, }, "status": { "description": "Status of the need.", "schema": {"type": ["string", "null"], "default": None}, "show_in_layout": True, + "allow_df": True, }, "tags": { "description": "List of tags.", "schema": {"type": "array", "items": {"type": "string"}, "default": []}, "show_in_layout": True, + "allow_df": True, }, "collapse": { "description": "Hide the meta-data information of the need.", @@ -138,6 +144,7 @@ class CoreFieldParameters(TypedDict): "schema": {"type": ["string", "null"], "default": None}, "show_in_layout": True, "exclude_external": True, + "allow_df": True, }, "arch": { "description": "Mapping of uml key to uml content.", @@ -167,12 +174,14 @@ class CoreFieldParameters(TypedDict): "type": { "description": "Type of the need.", "schema": {"type": "string", "default": ""}, + "allow_df": True, }, "type_name": { "description": "Name of the type.", "schema": {"type": "string", "default": ""}, "exclude_external": True, "exclude_import": True, + "allow_df": True, }, "type_prefix": { "description": "Prefix of the type.", @@ -180,6 +189,7 @@ class CoreFieldParameters(TypedDict): "exclude_json": True, "exclude_external": True, "exclude_import": True, + "allow_df": True, }, "type_color": { "description": "Hexadecimal color code of the type.", @@ -187,6 +197,7 @@ class CoreFieldParameters(TypedDict): "exclude_json": True, "exclude_external": True, "exclude_import": True, + "allow_df": True, }, "type_style": { "description": "Style of the type.", @@ -194,6 +205,7 @@ class CoreFieldParameters(TypedDict): "exclude_json": True, "exclude_external": True, "exclude_import": True, + "allow_df": True, }, "is_modified": { "description": "Whether the need was modified by needextend.", @@ -292,6 +304,7 @@ class CoreFieldParameters(TypedDict): "constraints": { "description": "List of constraint names, which are defined for this need.", "schema": {"type": "array", "items": {"type": "string"}, "default": []}, + "allow_df": True, }, "constraints_results": { "description": "Mapping of constraint name, to check name, to result.", diff --git a/sphinx_needs/functions/functions.py b/sphinx_needs/functions/functions.py index 7dd2cfc4c..892cc6a6e 100644 --- a/sphinx_needs/functions/functions.py +++ b/sphinx_needs/functions/functions.py @@ -19,7 +19,12 @@ from sphinx.util.tags import Tags from sphinx_needs.config import NeedsSphinxConfig -from sphinx_needs.data import NeedsInfoType, NeedsMutable, SphinxNeedsData +from sphinx_needs.data import ( + NeedsCoreFields, + NeedsInfoType, + NeedsMutable, + SphinxNeedsData, +) from sphinx_needs.debug import measure_time_func from sphinx_needs.logging import get_logger, log_warning from sphinx_needs.nodes import Need @@ -228,14 +233,19 @@ def resolve_dynamic_values(needs: NeedsMutable, app: Sphinx) -> None: - ``*args``: optional arguments (specified in the function string) - ``**kwargs``: optional keyword arguments (specified in the function string) """ + + config = NeedsSphinxConfig(app.config) + + allowed_fields: set[str] = { + *(k for k, v in NeedsCoreFields.items() if v.get("allow_df", False)), + *config.extra_options, + *(link["option"] for link in config.extra_links), + *config.global_options, + } + for need in needs.values(): for need_option in need: - if need_option in [ - "docname", - "lineno", - "content", - ]: - # dynamic values in this data are not allowed. + if need_option not in allowed_fields: continue if not isinstance(need[need_option], (list, set)): func_call: str | None = "init" @@ -288,18 +298,10 @@ def resolve_dynamic_values(needs: NeedsMutable, app: Sphinx) -> None: ) if func_call is None: new_values.append(element) + elif isinstance(func_return, (list, set)): + new_values += func_return else: - # Replace original function string with return value of function call - if isinstance(need[need_option], (str, int, float)): - new_values.append( - element.replace(f"[[{func_call}]]", str(func_return)) - ) - else: - if isinstance(need[need_option], (list, set)): - if isinstance(func_return, (list, set)): - new_values += func_return - else: - new_values += [func_return] + new_values += [func_return] need[need_option] = new_values From 19927a81ead3ba9bcde4350fd53d46317f5cb791 Mon Sep 17 00:00:00 2001 From: Chris Sewell Date: Mon, 27 Jan 2025 13:33:55 +0100 Subject: [PATCH 2/3] Update sphinx_needs/functions/functions.py --- sphinx_needs/functions/functions.py | 1 - 1 file changed, 1 deletion(-) diff --git a/sphinx_needs/functions/functions.py b/sphinx_needs/functions/functions.py index 892cc6a6e..d25537b42 100644 --- a/sphinx_needs/functions/functions.py +++ b/sphinx_needs/functions/functions.py @@ -240,7 +240,6 @@ def resolve_dynamic_values(needs: NeedsMutable, app: Sphinx) -> None: *(k for k, v in NeedsCoreFields.items() if v.get("allow_df", False)), *config.extra_options, *(link["option"] for link in config.extra_links), - *config.global_options, } for need in needs.values(): From 61adc01cdd8c1e11d8d70973d1d593ad38fef39b Mon Sep 17 00:00:00 2001 From: Chris Sewell Date: Mon, 27 Jan 2025 13:40:19 +0100 Subject: [PATCH 3/3] Update sphinx_needs/functions/functions.py --- sphinx_needs/functions/functions.py | 1 + 1 file changed, 1 insertion(+) diff --git a/sphinx_needs/functions/functions.py b/sphinx_needs/functions/functions.py index d25537b42..892cc6a6e 100644 --- a/sphinx_needs/functions/functions.py +++ b/sphinx_needs/functions/functions.py @@ -240,6 +240,7 @@ def resolve_dynamic_values(needs: NeedsMutable, app: Sphinx) -> None: *(k for k, v in NeedsCoreFields.items() if v.get("allow_df", False)), *config.extra_options, *(link["option"] for link in config.extra_links), + *config.global_options, } for need in needs.values():