useblocks / sphinx-needs

Adds needs/requirements to sphinx
https://sphinx-needs.readthedocs.io/en/latest/index.html
MIT License
211 stars 66 forks source link

Feature request: field from content #114

Closed ubmarco closed 4 years ago

ubmarco commented 4 years ago

I have a proposal for a new feature. My goal is to set a need's fields from its content. The idea is to support the following field definition style:

.. requirement:: Wheel requirement
    :id: wheel_4

    The car shall have 4 wheels. The following meta-data applies:
    ==============  ===
    Field           Value
    ==============  ===
    status          **[[set_field_from_content('status', 'implemented')]]**
    domain          **[[set_field_from_content('domain', 'chassis')]]**
    ==============  ===

This would set 2 fields, :status: implemented and :domain: chassis. The fields must be defined in conf.py using needs_extra_options. The function set_field_from_content must return the field value so it appears in the content text and the Sphinx/Docutils markup can be applied. I see the following benefits:

I already implemented that locally using dynamic functions, but it would be nice to have it as part of the extension. Here's my implementation:

# the new fields (status is already predefined)
needs_extra_options = {
    'domain': directives.unchanged,
}

def set_field_from_content(app, need, needs, *args, **kwargs):
    """
    This functions sets need[args[0]] = args[1] if the input data in args are valid.

    The function returns args[1] which will be inserted into the content.
    That way any RST markup will still be applied to the returned text.
    :param app: sphinx app
    :param need: current need
    :param needs: dictionary of all needs. Key is the need id
    :return: value to return into the content
    :rtype: str
    """
    if kwargs:
        raise ValueError('The dynamic function set_field_from_content does not support kwargs')
    if len(args) != 2:
        raise ValueError('The dynamic function set_field_from_content must have exactly 2 arguments: '
                         'field and its value')
    field = args[0]
    value = args[1]
    if field in need:
        need[field] = value
    else:
        raise ValueError(f"The field {field} does not exist on need {need['id']}")
    return value

needs_functions = [set_field_from_content]

The function name can, however, be shortened to 'set_field'. I think that would make it more readable.

ubmarco commented 4 years ago

I just realized I have another option to achieve my goals:

.. requirement:: Wheel requirement
    :id: wheel_4
    :status: implemented
    :domain: chassis

    The car shall have 4 wheels. The following meta-data applies:
    ==============  ===
    Field           Value
    ==============  ===
    status          **[[copy('status')]]**
    domain          **[[copy('domain')]]**
    ==============  ===

With this solution, I don't need the proposed feature. It looks cleaner to me because fields are explicitly set. I also have the markup feature, so in above example the copied fields will be rendered bold. I'll close the issue.