Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow to specify choices as dictionary instead of list #36

Merged
merged 2 commits into from
Sep 22, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions changelogs/fragments/36-choices-dict.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
minor_changes:
- "Allow to specify choices as dictionary instead of list (https://github.com/ansible-community/antsibull-docs/pull/36)."
82 changes: 82 additions & 0 deletions src/antsibull_docs/data/docsite/macros/choiceslist.rst.j2
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
{#
Copyright (c) Ansible Project
GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
SPDX-License-Identifier: GPL-3.0-or-later
#}

{% macro in_rst(choices, default_value=None, has_no_default=False) %}
{% if choices is mapping %}
{% for choice, desc in choices | dictsort %}
{# Turn boolean values in 'true' and 'false' values #}
{% if choice is sameas true %}
{% set choice = 'true' %}
{% elif choice is sameas false %}
{% set choice = 'false' %}
{% endif %}
{% if not has_no_default and ((default_value is not list and default_value == choice) or (default_value is list and choice in default_value)) %}
- :ansible-option-default-bold:`@{ choice | rst_escape(escape_ending_whitespace=true) }@` :ansible-option-default:`(default)`\ :
{% else %}
- :ansible-option-choices-entry:`@{ choice | rst_escape(escape_ending_whitespace=true) }@`\ :
{% endif %}
{% for par in desc %}
@{ par | rst_ify | indent(8) }@

{% endfor %}
{% endfor %}
{% else %}
{% for choice in choices %}
{# Turn boolean values in 'true' and 'false' values #}
{% if choice is sameas true %}
{% set choice = 'true' %}
{% elif choice is sameas false %}
{% set choice = 'false' %}
{% endif %}
{% if not has_no_default and ((default_value is not list and default_value == choice) or (default_value is list and choice in default_value)) %}
- :ansible-option-default-bold:`@{ choice | rst_escape(escape_ending_whitespace=true) }@` :ansible-option-default:`← (default)`
{% else %}
- :ansible-option-choices-entry:`@{ choice | rst_escape(escape_ending_whitespace=true) }@`
{% endif %}
{% endfor %}
{% endif %}
{% endmacro %}


{% macro in_html(choices, default_value=None, has_no_default=False) %}
<ul class="simple">
{% if choices is mapping %}
{% for choice, desc in choices | dictsort %}
{# Turn boolean values in 'true' and 'false' values #}
{% if choice is sameas true %}
{% set choice = 'true' %}
{% elif choice is sameas false %}
{% set choice = 'false' %}
{% endif %}
<li>
{% if not has_no_default and ((default_value is not list and default_value == choice) or (default_value is list and choice in default_value)) %}
<p><span class="ansible-option-default-bold">@{ choice | escape }@</span> <span class="ansible-option-default">(default)</span>:
{% else %}
<p><span class="ansible-option-choices-entry">@{ choice | escape }@</span>:
{% endif %}
@{ desc | first | default('') | html_ify | indent(10, blank=true) }@</p>
{% for line in desc[1:] %}
<p>@{ line | html_ify | indent(10, blank=true) }@</p>
{% endfor %}
</li>
{% endfor %}
{% else %}
{% for choice in choices %}
{# Turn boolean values in 'true' and 'false' values #}
{% if choice is sameas true %}
{% set choice = 'true' %}
{% elif choice is sameas false %}
{% set choice = 'false' %}
{% endif %}
{% if not has_no_default and ((default_value is not list and default_value == choice) or (default_value is list and choice in default_value)) %}
<li><p><span class="ansible-option-default-bold">@{ choice | escape }@</span> <span class="ansible-option-default">← (default)</span></p></li>
{% else %}
<li><p><span class="ansible-option-choices-entry">@{ choice | escape }@</span></p></li>
{% endif %}
{% endfor %}
{% endif %}
</ul>
{% endmacro %}
34 changes: 5 additions & 29 deletions src/antsibull_docs/data/docsite/macros/parameters.rst.j2
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
SPDX-License-Identifier: GPL-3.0-or-later
#}

{% from 'macros/choiceslist.rst.j2' import in_rst as choices_rst %}
{% from 'macros/choiceslist.rst.j2' import in_html as choices_html %}
{% from 'macros/deprecates.rst.j2' import in_rst as deprecates_rst with context %}
{% from 'macros/deprecates.rst.j2' import in_html as deprecates_html with context %}
{% from 'macros/version_added.rst.j2' import version_added_rst, version_added_html %}
Expand Down Expand Up @@ -72,7 +74,7 @@

{% for i in range(1, loop.depth) %}<div class="ansible-option-indent-desc"></div>{% endfor %}<div class="ansible-option-cell">

{% for desc in value['description'] %}
{% for desc in value['description'] %}
@{ desc | replace('\n', '\n ') | rst_ify | indent(6) }@

{% endfor %}
Expand All @@ -93,19 +95,7 @@

:ansible-option-choices:`Choices:`

{% for choice in value['choices'] %}
{# Turn boolean values in 'true' and 'false' values #}
{% if choice is sameas true %}
{% set choice = 'true' %}
{% elif choice is sameas false %}
{% set choice = 'false' %}
{% endif %}
{% if (value['default'] is not list and value['default'] == choice) or (value['default'] is list and choice in value['default']) %}
- :ansible-option-default-bold:`@{ choice | rst_escape(escape_ending_whitespace=true) }@` :ansible-option-default:`← (default)`
{% else %}
- :ansible-option-choices-entry:`@{ choice | rst_escape(escape_ending_whitespace=true) }@`
{% endif %}
{% endfor %}
@{ choices_rst(value['choices'], value['default']) }@
{% endif %}
{# Show default value, when multiple choice or no choices #}
{% if value['default'] is not none and value['default'] not in value['choices'] %}
Expand Down Expand Up @@ -239,21 +229,7 @@
{# Show possible choices and highlight details #}
{% if value['choices'] %}
<p class="ansible-option-line"><span class="ansible-option-choices">Choices:</span></p>
<ul class="simple">
{% for choice in value['choices'] %}
{# Turn boolean values in 'true' and 'false' values #}
{% if choice is sameas true %}
{% set choice = 'true' %}
{% elif choice is sameas false %}
{% set choice = 'false' %}
{% endif %}
{% if (value['default'] is not list and value['default'] == choice) or (value['default'] is list and choice in value['default']) %}
<li><p><span class="ansible-option-default-bold">@{ choice | escape }@</span> <span class="ansible-option-default">← (default)</span></p></li>
{% else %}
<li><p><span class="ansible-option-choices-entry">@{ choice | escape }@</span></p></li>
{% endif %}
{% endfor %}
</ul>
@{ choices_html(value['choices'], value['default']) }@
{% endif %}
{# Show default value, when multiple choice or no choices #}
{% if value['default'] is not none and value['default'] not in value['choices'] %}
Expand Down
38 changes: 7 additions & 31 deletions src/antsibull_docs/data/docsite/macros/returnvalues.rst.j2
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
SPDX-License-Identifier: GPL-3.0-or-later
#}

{% from 'macros/choiceslist.rst.j2' import in_rst as choices_rst %}
{% from 'macros/choiceslist.rst.j2' import in_html as choices_html %}
{% from 'macros/version_added.rst.j2' import version_added_rst, version_added_html %}

{% macro in_rst(elements) %}
Expand Down Expand Up @@ -58,36 +60,24 @@

{% for i in range(1, loop.depth) %}<div class="ansible-option-indent-desc"></div>{% endfor %}<div class="ansible-option-cell">

{% for desc in value['description'] %}
{% for desc in value['description'] %}
@{ desc | rst_ify | indent(6) }@

{% endfor %}
{% if value['returned'] %}
{% if value['returned'] %}

.. rst-class:: ansible-option-line

:ansible-option-returned-bold:`Returned:` @{ value['returned'] | rst_ify | indent(6) }@
{% endif %}
{% endif %}
{# Show possible choices and highlight details #}
{% if value['choices'] %}

.. rst-class:: ansible-option-line

:ansible-option-choices:`Can only return:`

{% for choice in value['choices'] %}
{# Turn boolean values in 'true' and 'false' values #}
{% if choice is sameas true %}
{% set choice = 'true' %}
{% elif choice is sameas false %}
{% set choice = 'false' %}
{% endif %}
{% if (value['default'] is not list and value['default'] == choice) or (value['default'] is list and choice in value['default']) %}
- :ansible-option-default-bold:`@{ choice | rst_escape }@` :ansible-option-default:`← (default)`
{% else %}
- :ansible-option-choices-entry:`@{ choice | rst_escape }@`
{% endif %}
{% endfor %}
@{ choices_rst(value['choices'], has_no_default=True) }@
{% endif %}
{% if value['sample'] is not none %}

Expand Down Expand Up @@ -152,21 +142,7 @@
{# Show possible choices and highlight details #}
{% if value['choices'] %}
<p class="ansible-option-line"><span class="ansible-option-choices">Can only return:</span></p>
<ul class="simple">
{% for choice in value['choices'] %}
{# Turn boolean values in 'true' and 'false' values #}
{% if choice is sameas true %}
{% set choice = 'true' %}
{% elif choice is sameas false %}
{% set choice = 'false' %}
{% endif %}
{% if (value['default'] is not list and value['default'] == choice) or (value['default'] is list and choice in value['default']) %}
<li><p><span class="ansible-option-default-bold">@{ choice | escape }@</span> <span class="ansible-option-default">← (default)</span></p></li>
{% else %}
<li><p><span class="ansible-option-choices-entry">@{ choice | escape }@</span></p></li>
{% endif %}
{% endfor %}
</ul>
@{ choices_html(value['choices'], has_no_default=True) }@
{% endif %}
{% if value['sample'] is not none %}
<p class="ansible-option-line ansible-option-sample"><span class="ansible-option-sample-bold">Sample:</span> @{ value['sample'] | antsibull_to_json | escape | indent(6, blank=true) }@</p>
Expand Down
17 changes: 14 additions & 3 deletions src/antsibull_docs/schemas/docs/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -304,7 +304,7 @@ def normalize_return_type_names(obj):


def normalize_value(values: t.Dict[str, t.Any], field: str, # noqa: C901
is_list_of_values: bool = False) -> None:
is_list_of_values: bool = False, accept_dict: bool = False) -> None:
if 'type' not in values or values.get(field) is None:
return

Expand All @@ -317,8 +317,12 @@ def normalize_value(values: t.Dict[str, t.Any], field: str, # noqa: C901
elements_name = normalize_option_type_names(values.get('elements'))
elements_checker = TYPE_CHECKERS.get(elements_name)

descs = None
if not is_list_of_values:
value = [value]
elif accept_dict and isinstance(value, dict):
descs = list(value.values())
value = list(value)
elif not isinstance(value, list):
return

Expand All @@ -339,6 +343,9 @@ def normalize_value(values: t.Dict[str, t.Any], field: str, # noqa: C901

if not is_list_of_values:
value = value[0]
elif descs is not None:
value = dict(zip(value, descs))

values[field] = value


Expand Down Expand Up @@ -444,7 +451,7 @@ def merge_typo_names(cls, values):
class OptionsSchema(BaseModel):
description: t.List[str]
aliases: t.List[str] = []
choices: t.List[t.Union[str, None]] = []
choices: t.Union[t.List[t.Any], t.Dict[t.Any, t.List[str]]] = []
default: t.Any = None # JSON value
elements: str = OPTION_TYPE_F
required: bool = False
Expand Down Expand Up @@ -508,8 +515,12 @@ def normalize_option_type(cls, obj):
@p.root_validator(pre=True)
# pylint:disable=no-self-argument,no-self-use
def normalize_default_choices(cls, values):
if isinstance(values.get('choices'), dict):
for k, v in values['choices'].items():
values['choices'][k] = list_from_scalars(v)
normalize_value(values, 'default')
normalize_value(values, 'choices', is_list_of_values=values.get('type') != 'list')
normalize_value(
values, 'choices', is_list_of_values=values.get('type') != 'list', accept_dict=True)
return values


Expand Down
12 changes: 11 additions & 1 deletion src/antsibull_docs/schemas/docs/plugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ class ReturnSchema(BaseModel):
"""Schema of plugin return data docs."""

description: t.List[str]
choices: t.List[str] = []
choices: t.Union[t.List[t.Any], t.Dict[t.Any, t.List[str]]] = []
elements: str = RETURN_TYPE_F
returned: str = 'success'
sample: t.Any = None # JSON value
Expand Down Expand Up @@ -133,6 +133,16 @@ def normalize_sample(cls, values):
pass
return values

@p.root_validator(pre=True)
# pylint:disable=no-self-argument,no-self-use
def normalize_choices(cls, values):
if isinstance(values.get('choices'), dict):
for k, v in values['choices'].items():
values['choices'][k] = list_from_scalars(v)
normalize_value(
values, 'choices', is_list_of_values=values.get('type') != 'list', accept_dict=True)
return values


class InnerReturnSchema(ReturnSchema):
"""Nested return schema which allows leaving out description."""
Expand Down