Dynamic functions¶
Sphinx-Needs provides a mechanism to set dynamic data for need-options during generation. We do this by giving an author the possibility to set a function call to a predefined function, which calculates the final result/value for the option.
For instance, you can use the feature if the status of a requirement depends on linked test cases and their status. Or if you will request specific data from an external server like JIRA.
needtable
The options style_row of needtable also support dynamic function execution. In this case, the function gets executed with the found need for each row.
This allows you to set row and column specific styles such as, set a row background to red, if a need-status is failed.
Example¶
.. req:: my test requirement
:id: df_1
:status: open
This need has the id **[[copy("id")]]** and status **[[copy("status")]]**.
This need has id df_1 and status open. |
Built-in functions¶
The following functions are available in all Sphinx-Needs installations.
Note
The parameters app
, need
and needs
of the following functions are set automatically.
test¶
- test(app: Sphinx, need: NeedsInfoType, needs: dict[str, NeedsInfoType], *args: Any, **kwargs: Any) str ¶
Test function for dynamic functions in sphinx needs.
Collects every given args and kwargs and returns a single string, which contains their values/keys.
.. req:: test requirement [[test('arg_1', [1,2,3], my_keyword='awesome')]]
Test output of need R_A6A4E. args: ('arg_1', [1, 2, 3]). kwargs: {'my_keyword': 'awesome'}
- Returns:¶
single test string
echo¶
- echo(app: Sphinx, need: NeedsInfoType, needs: dict[str, NeedsInfoType], text: str, *args: Any, **kwargs: Any) str ¶
New in version 0.6.3.
Just returns the given string back. Mostly useful for tests.
A nice :need_func:`[[echo("first")]] test` for need_func.
Result: A nice first test for need_func.
copy¶
-
copy(app: Sphinx, need: NeedsInfoType, needs: dict[str, NeedsInfoType], option: str, need_id: str | None =
None
, lower: bool =False
, upper: bool =False
, filter: str | None =None
) Any ¶ Copies the value of one need option to another
.. req:: copy-example :id: copy_1 :tags: tag_1, tag_2, tag_3 :status: open .. spec:: copy-example implementation :id: copy_2 :status: [[copy("status", "copy_1")]] :links: copy_1 :comment: [[copy("id")]] Copies status of ``copy_1`` to own status. Sets also a comment, which copies the id of own need. .. test:: test of specification and requirement :id: copy_3 :links: copy_2; [[copy('links', 'copy_2')]] :tags: [[copy('tags', 'copy_1')]] Set own link to ``copy_2`` and also copies all links from it. Also copies all tags from copy_1.
Copies status of
copy_1
to own status. Sets also a comment, which copies the id of own need.Set own link to
copy_2
and also copies all links from it.Also copies all tags from copy_1.
If the filter_string needs to compare a value from the current need and the value is unknown yet, you can reference the valued field by using
current_need["my_field"]
inside the filter string. Small example:.. test:: test of current_need value :id: copy_4 The es the title of the first need found under the same highest section (headline): [[copy('title', filter='current_need["sections"][-1]==sections[-1]')]]
The following copy command copies the title of the first need found under the same highest section (headline):
my test requirement
This filter possibilities get really powerful in combination with needs_global_options.
- Parameters:¶
- option: str¶
Name of the option to copy
- need_id: str | None =
None
¶ id of the need, which contains the source option. If None, current need is taken
- upper: bool =
False
¶ Is set to True, copied value will be uppercase
- lower: bool =
False
¶ Is set to True, copied value will be lowercase
- filter: str | None =
None
¶ Filter string, which first result is used as copy source.
- Returns:¶
string of copied need option
check_linked_values¶
-
check_linked_values(app: Sphinx, need: NeedsInfoType, needs: dict[str, NeedsInfoType], result: Any, search_option: str, search_value: Any, filter_string: str | None =
None
, one_hit: bool =False
) Any ¶ Returns a specific value, if for all linked needs a given option has a given value.
The linked needs can be filtered by using the
filter
option.If
one_hit
is set to True, only one linked need must have a positive match for the searched value.Examples
Needs used as input data
.. req:: Input A :id: clv_A :status: in progress .. req:: Input B :id: clv_B :status: in progress .. spec:: Input C :id: clv_C :status: closed
Example 1: Positive check
Status gets set to progress.
.. spec:: result 1: Positive check :links: clv_A, clv_B :status: [[check_linked_values('progress', 'status', 'in progress' )]]
Example 2: Negative check
Status gets not set to progress, because status of linked need clv_C does not match “in progress”.
.. spec:: result 2: Negative check :links: clv_A, clv_B, clv_C :status: [[check_linked_values('progress', 'status', 'in progress' )]]
Example 3: Positive check thanks of used filter
status gets set to progress, because linked need clv_C is not part of the filter.
.. spec:: result 3: Positive check thanks of used filter :links: clv_A, clv_B, clv_C :status: [[check_linked_values('progress', 'status', 'in progress', 'type == "req" ' )]]
Example 4: Positive check thanks of one_hit option
Even clv_C has not the searched status, status gets anyway set to progress. That’s because
one_hit
is used so that only one linked need must have the searched value... spec:: result 4: Positive check thanks of one_hit option :links: clv_A, clv_B, clv_C :status: [[check_linked_values('progress', 'status', 'in progress', one_hit=True )]]
Result 5: Two checks and a joint status Two checks are performed and both are positive. So their results get joined.
.. spec:: result 5: Two checks and a joint status :links: clv_A, clv_B, clv_C :status: [[check_linked_values('progress', 'status', 'in progress', one_hit=True )]] [[check_linked_values('closed', 'status', 'closed', one_hit=True )]]
- Parameters:¶
- result: Any¶
value, which gets returned if all linked needs have parsed the checks
- search_option: str¶
option name, which is used n linked needs for the search
- search_value: Any¶
value, which an option of a linked need must match
- filter_string: str | None =
None
¶ Checks are only performed on linked needs, which pass the defined filter
- one_hit: bool =
False
¶ If True, only one linked need must have a positive check
- Returns:¶
result, if all checks are positive
calc_sum¶
-
calc_sum(app: Sphinx, need: NeedsInfoType, needs: dict[str, NeedsInfoType], option: str, filter: str | None =
None
, links_only: bool =False
) float ¶ Sums the values of a given option in filtered needs up to single number.
Useful e.g. for calculating the amount of needed hours for implementation of all linked specification needs.
Input data
Example 2
.. req:: Result 1 :amount: [[calc_sum("hours")]]
Example 2
.. req:: Result 2 :amount: [[calc_sum("hours", "hours.isdigit() and float(hours) > 10")]]
Example 3
.. req:: Result 3 :links: sum_input_1; sum_input_3 :amount: [[calc_sum("hours", links_only="True")]]
Example 4
.. req:: Result 4 :links: sum_input_1; sum_input_3 :amount: [[calc_sum("hours", "hours.isdigit() and float(hours) > 10", "True")]]
links_from_content¶
-
links_from_content(app: Sphinx, need: NeedsInfoType, needs: dict[str, NeedsInfoType], need_id: str | None =
None
, filter: str | None =None
) list[str] ¶ Extracts links from content of a need.
All need-links set by using
:need:`NEED_ID`
get extracted.Same links are only added once.
Example:
This specification cares about the realisation of:
Links retrieved from content of Test spec (CON_SPEC_1)
Used code of CON_SPEC_1:
.. spec:: Test spec :id: CON_SPEC_1 :links: [[links_from_content()]] This specification cares about the realisation of: * :need:`CON_REQ_1` * :need:`CON_REQ_2` .. spec:: Test spec 2 :id: CON_SPEC_2 :links: [[links_from_content('CON_SPEC_1')]] Links retrieved from content of :need:`CON_SPEC_1`
Develop own functions¶
Registration¶
You must register every dynamic function by using the needs_functions configuration parameter inside your conf.py file:
def my_own_function(app, need, needs):
return "Awesome"
needs_functions = [my_own_function]
Warning
Assigning a function to a Sphinx option will deactivate the incremental build feature of Sphinx. Please use the Sphinx-Needs API and read Incremental build support for details.
Recommended: You can use the following approach we used in our conf.py file to register dynamic functions:
from sphinx_needs.api import add_dynamic_function
def my_function(app, need, needs, *args, **kwargs):
# Do magic here
return "some data"
def setup(app):
add_dynamic_function(app, my_function)
Reference function¶
def test(app, need, needs, *args, **kwargs):
"""
:param app: sphinx app
:param need: current need
:param needs: dictionary of all needs. Key is the need id
:return: str,int,float or list of elements of type str,int,float
"""
# where the magic happens
return "awesome"
You can call the defined function via:
.. req:: test requirement
:status: [[test()]]
The parameters app
, need
and needs
are set automatically. You are free to add other parameters, which
must be of type str, int, float and list.
need structure¶
need = {
'docname': str: document name,
'lineno': int: linenumber,
'links_back': list: list of incoming links (see restrictions),
'target_node': node: sphinx target node for internal references,
'type': str: short name of type,
'type_name': str: long name of type,
'type_prefix': str: name of type prefix,
'type_color': str: type color,
'type_style': str: type style,
'status': str: status,
'tags': list: list of tags,
'id': str: id,
'links': list: list of outgoing links,
'title': str: trimmed title of need,
'full_title': str: full title of string,
'content': str: unparsed need content,
'collapse': bool: true if meta data shall be collapsed,
'hide': bool: true if complete need shall be hidden,
'hide_tags': bool: if tags shall be hidden,
'hide_status': bool: true if status shall be hidden,
}
Adding new keywords to need object will be treated as extra_option in need meta area.
Restrictions¶
incoming_links¶
Incoming links are not available when dynamic functions gets calculated.
That’s because a dynamic function can change outgoing links, so that the incoming links of the target need will be recalculated. This is automatically done but not until all dynamic functions are resolved.