Dynamic functions

Sphinx-Needs provides a mechanism to set dynamic data for need-options during generation. This is realised by giving an author the possibility to set a function call to a predefined function, which calculates the final result/value for the option.

This can be useful, if for instance the status of a requirement depends on linked test cases and their status. Or if specific data shall be requested from an external server like JIRA.

needtable

The options style_row of needtable are also supporting dynamic function execution. In this case the function will get executed with the found need for each row.

This allows to set row and column specific styles and e.g. 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")]]**.
Requirement: my test requirement df_1 _images/arrow-right-circle.svg
status: open

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, need, needs, *args, **kwargs)

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')]]
Requirement: test requirement R_A6A4E _images/arrow-right-circle.svg

Test output of need R_A6A4E. args: ('arg_1', [1, 2, 3]). kwargs: {'my_keyword': 'awesome'}

Returns

single test string

echo

echo(app, need, needs, text, *args, **kwargs)

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, need, needs, option, need_id=None, lower=False, upper=False, filter=None)

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.
Requirement: copy-example copy_1 _images/arrow-right-circle.svg
status: open
tags: tag_1, tag_2, tag_3
links incoming: copy_2, copy_3
Specification: copy-example implementation copy_2 _images/arrow-right-circle.svg
status: open
comment: copy_2
links outgoing: copy_1
links incoming: copy_3

Copies status of copy_1 to own status. Sets also a comment, which copies the id of own need.

Test Case: test of specification and requirement copy_3 _images/arrow-right-circle.svg
tags: tag_1, tag_2, tag_3
links outgoing: copy_2, copy_1

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 following copy command copies the title of the first need found under the same highest
   section (headline):

   [[copy('title', filter='current_need["sections"][-1]==sections[-1]')]]
Test Case: test of current_need value copy_4 _images/arrow-right-circle.svg

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 – Name of the option to copy

  • need_id – id of the need, which contains the source option. If None, current need is taken

  • upper – Is set to True, copied value will be uppercase

  • lower – Is set to True, copied value will be lowercase

  • filterFilter string, which first result is used as copy source.

Returns

string of copied need option

check_linked_values

check_linked_values(app, need, needs, result, search_option, search_value, filter_string=None, one_hit=False)

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
Requirement: Input A clv_A _images/arrow-right-circle.svg
status: in progress
links incoming: clv_1, clv_2, clv_3, clv_4, clv_5
Requirement: Input B clv_B _images/arrow-right-circle.svg
status: in progress
links incoming: clv_1, clv_2, clv_3, clv_4, clv_5
Specification: Input C clv_C _images/arrow-right-circle.svg
status: closed
links incoming: clv_2, clv_3, clv_4, clv_5

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' )]]
Specification: result 1: Positive check clv_1 _images/arrow-right-circle.svg
status: progress
links outgoing: clv_A, clv_B

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' )]]
Specification: result 2: Negative check clv_2 _images/arrow-right-circle.svg
links outgoing: clv_A, clv_B, clv_C

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" ' )]]
Specification: result 3: Positive check thanks of used filter clv_3 _images/arrow-right-circle.svg
status: progress
links outgoing: clv_A, clv_B, clv_C

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 )]]
Specification: result 4: Positive check thanks of one_hit option clv_4 _images/arrow-right-circle.svg
status: progress
links outgoing: clv_A, clv_B, clv_C

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 )]]
Specification: result 5: Two checks and a joint status clv_5 _images/arrow-right-circle.svg
status: progress closed
links outgoing: clv_A, clv_B, clv_C
Parameters
  • result – value, which gets returned if all linked needs have parsed the checks

  • search_option – option name, which is used n linked needs for the search

  • search_value – value, which an option of a linked need must match

  • filter_string – Checks are only performed on linked needs, which pass the defined filter

  • one_hit – If True, only one linked need must have a positive check

Returns

result, if all checks are positive

calc_sum

calc_sum(app, need, needs, option, filter=None, links_only=False)

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

Specification: Do this sum_input_1 _images/arrow-right-circle.svg
hours: 7
links incoming: R_D0791, R_3C95C
Specification: Do that sum_input_2 _images/arrow-right-circle.svg
hours: 15
Specification: Do too much sum_input_3 _images/arrow-right-circle.svg
hours: 110
links incoming: R_D0791, R_3C95C

Example 2

.. req:: Result 1
   :amount: [[calc_sum("hours")]]
Requirement: Result 1 R_F7DEB _images/arrow-right-circle.svg
amount: 147.0

Example 2

.. req:: Result 2
   :amount: [[calc_sum("hours", "hours.isdigit() and float(hours) > 10")]]
Requirement: Result 2 R_96D5E _images/arrow-right-circle.svg
amount: 137.0

Example 3

.. req:: Result 3
   :links: sum_input_1; sum_input_3
   :amount: [[calc_sum("hours", links_only="True")]]
Requirement: Result 3 R_D0791 _images/arrow-right-circle.svg
amount: 117.0
links outgoing: sum_input_1, sum_input_3

Example 4

.. req:: Result 4
   :links: sum_input_1; sum_input_3
   :amount: [[calc_sum("hours", "hours.isdigit() and float(hours) > 10", "True")]]
Requirement: Result 4 R_3C95C _images/arrow-right-circle.svg
amount: 110.0
links outgoing: sum_input_1, sum_input_3
Parameters
  • option – Options, from which the numbers shall be taken

  • filter – Filter string, which all needs must passed to get their value added.

  • links_only – If “True”, only linked needs are taken into account.

Returns

A float number

Develop own functions

Registration

Every dynamic function must be registered by using configuration parameter needs_functions 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.

Better use the following way in our conf.py file:

from sphinxcontrib.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"

Such a defined function can be called via:

.. req:: test requirement
   :status: [[test()]]

The parameters app, need and needs are set automatically. You are free to add other parameters, which are allowed to 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