Skip to content

Commit d567311

Browse files
authored
Convert 'default', 'choices', 'sample' to type; change booleans to 'true' and 'false' (#19)
* Normalize booleans to 'true' and 'false' instead of 'yes' and 'no'. * Began with default / sample / choices format adjustment. * Vendor code from ansible/ansible. * Add changelog fragment. * Fix tests.
1 parent 878eebb commit d567311

File tree

9 files changed

+631
-30
lines changed

9 files changed

+631
-30
lines changed

LICENSES/BSD-2-Clause.txt

+8
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
2+
3+
1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
4+
5+
2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
6+
7+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
8+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
minor_changes:
2+
- "Ensure that values for ``default``, ``choices``, and ``sample`` use the types specified for the option / return value (https://github.com/ansible-community/antsibull-docs/pull/19)."
3+
- "Use ``true`` and ``false`` for booleans instead of ``yes`` and ``no`` (https://github.com/ansible-community/community-topics/issues/116, https://github.com/ansible-community/antsibull-docs/pull/19)."

src/antsibull_docs/data/docsite/macros/parameters.rst.j2

+14-14
Original file line numberDiff line numberDiff line change
@@ -76,14 +76,14 @@
7676

7777
{% endfor %}
7878
{# default / choices #}
79-
{# Turn boolean values in 'yes' and 'no' values #}
79+
{# Turn boolean values in 'true' and 'false' values #}
8080
{% if value['default'] is sameas true %}
81-
{% set _x = value.update({'default': 'yes'}) %}
81+
{% set _x = value.update({'default': 'true'}) %}
8282
{% elif value['default'] is not none and value['default'] is sameas false %}
83-
{% set _x = value.update({'default': 'no'}) %}
83+
{% set _x = value.update({'default': 'false'}) %}
8484
{% endif %}
8585
{% if value['type'] == 'bool' %}
86-
{% set _x = value.update({'choices': ['no', 'yes']}) %}
86+
{% set _x = value.update({'choices': ['false', 'true']}) %}
8787
{% endif %}
8888
{# Show possible choices and highlight details #}
8989
{% if value['choices'] %}
@@ -93,11 +93,11 @@
9393
:ansible-option-choices:`Choices:`
9494

9595
{% for choice in value['choices'] %}
96-
{# Turn boolean values in 'yes' and 'no' values #}
96+
{# Turn boolean values in 'true' and 'false' values #}
9797
{% if choice is sameas true %}
98-
{% set choice = 'yes' %}
98+
{% set choice = 'true' %}
9999
{% elif choice is sameas false %}
100-
{% set choice = 'no' %}
100+
{% set choice = 'false' %}
101101
{% endif %}
102102
{% if (value['default'] is not list and value['default'] == choice) or (value['default'] is list and choice in value['default']) %}
103103
- :ansible-option-default-bold:`@{ choice | rst_escape(escape_ending_whitespace=true) }@` :ansible-option-default:`← (default)`
@@ -226,25 +226,25 @@
226226
<p>@{ desc | html_ify | indent(6, blank=true) }@</p>
227227
{% endfor %}
228228
{# default / choices #}
229-
{# Turn boolean values in 'yes' and 'no' values #}
229+
{# Turn boolean values in 'true' and 'false' values #}
230230
{% if value['default'] is sameas true %}
231-
{% set _x = value.update({'default': 'yes'}) %}
231+
{% set _x = value.update({'default': 'true'}) %}
232232
{% elif value['default'] is not none and value['default'] is sameas false %}
233-
{% set _x = value.update({'default': 'no'}) %}
233+
{% set _x = value.update({'default': 'false'}) %}
234234
{% endif %}
235235
{% if value['type'] == 'bool' %}
236-
{% set _x = value.update({'choices': ['no', 'yes']}) %}
236+
{% set _x = value.update({'choices': ['false', 'true']}) %}
237237
{% endif %}
238238
{# Show possible choices and highlight details #}
239239
{% if value['choices'] %}
240240
<p class="ansible-option-line"><span class="ansible-option-choices">Choices:</span></p>
241241
<ul class="simple">
242242
{% for choice in value['choices'] %}
243-
{# Turn boolean values in 'yes' and 'no' values #}
243+
{# Turn boolean values in 'true' and 'false' values #}
244244
{% if choice is sameas true %}
245-
{% set choice = 'yes' %}
245+
{% set choice = 'true' %}
246246
{% elif choice is sameas false %}
247-
{% set choice = 'no' %}
247+
{% set choice = 'false' %}
248248
{% endif %}
249249
{% if (value['default'] is not list and value['default'] == choice) or (value['default'] is list and choice in value['default']) %}
250250
<li><p><span class="ansible-option-default-bold">@{ choice | escape }@</span> <span class="ansible-option-default">← (default)</span></p></li>

src/antsibull_docs/data/docsite/macros/returnvalues.rst.j2

+6-6
Original file line numberDiff line numberDiff line change
@@ -74,11 +74,11 @@
7474
:ansible-option-choices:`Can only return:`
7575

7676
{% for choice in value['choices'] %}
77-
{# Turn boolean values in 'yes' and 'no' values #}
77+
{# Turn boolean values in 'true' and 'false' values #}
7878
{% if choice is sameas true %}
79-
{% set choice = 'yes' %}
79+
{% set choice = 'true' %}
8080
{% elif choice is sameas false %}
81-
{% set choice = 'no' %}
81+
{% set choice = 'false' %}
8282
{% endif %}
8383
{% if (value['default'] is not list and value['default'] == choice) or (value['default'] is list and choice in value['default']) %}
8484
- :ansible-option-default-bold:`@{ choice | rst_escape }@` :ansible-option-default:`← (default)`
@@ -152,11 +152,11 @@
152152
<p class="ansible-option-line"><span class="ansible-option-choices">Can only return:</span></p>
153153
<ul class="simple">
154154
{% for choice in value['choices'] %}
155-
{# Turn boolean values in 'yes' and 'no' values #}
155+
{# Turn boolean values in 'true' and 'false' values #}
156156
{% if choice is sameas true %}
157-
{% set choice = 'yes' %}
157+
{% set choice = 'true' %}
158158
{% elif choice is sameas false %}
159-
{% set choice = 'no' %}
159+
{% set choice = 'false' %}
160160
{% endif %}
161161
{% if (value['default'] is not list and value['default'] == choice) or (value['default'] is list and choice in value['default']) %}
162162
<li><p><span class="ansible-option-default-bold">@{ choice | escape }@</span> <span class="ansible-option-default">← (default)</span></p></li>

src/antsibull_docs/schemas/docs/base.py

+82-5
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,20 @@ def handle_renamed_attribute(cls, values):
128128
import pydantic as p
129129
import yaml
130130

131+
from antsibull_docs.vendored.ansible import (
132+
check_type_bits,
133+
check_type_bool,
134+
check_type_bytes,
135+
check_type_dict,
136+
check_type_float,
137+
check_type_int,
138+
check_type_jsonarg,
139+
check_type_list,
140+
check_type_raw,
141+
check_type_str,
142+
)
143+
144+
131145
_SENTINEL = object()
132146

133147

@@ -271,6 +285,62 @@ def normalize_return_type_names(obj):
271285
return obj
272286

273287

288+
TYPE_CHECKERS = {
289+
'str': check_type_str,
290+
'list': check_type_list,
291+
'dict': check_type_dict,
292+
'bool': check_type_bool,
293+
'int': check_type_int,
294+
'float': check_type_float,
295+
'path': check_type_str, # we intentionally use check_type_str here
296+
'tmppath': check_type_str, # we intentionally use check_type_str here
297+
'raw': check_type_raw,
298+
'jsonarg': check_type_jsonarg,
299+
'json': check_type_jsonarg,
300+
'bytes': check_type_bytes,
301+
'bits': check_type_bits,
302+
}
303+
304+
305+
def normalize_value(values: t.Dict[str, t.Any], field: str, # noqa: C901
306+
is_list_of_values: bool = False) -> None:
307+
if 'type' not in values or values.get(field) is None:
308+
return
309+
310+
value = values[field]
311+
type_name = normalize_option_type_names(values['type'])
312+
type_checker = TYPE_CHECKERS.get(type_name)
313+
if type_checker is None:
314+
return
315+
316+
elements_name = normalize_option_type_names(values.get('elements'))
317+
elements_checker = TYPE_CHECKERS.get(elements_name)
318+
319+
if not is_list_of_values:
320+
value = [value]
321+
elif not isinstance(value, list):
322+
return
323+
324+
for i, v in enumerate(value):
325+
try:
326+
v = type_checker(v)
327+
except Exception as exc:
328+
# pylint:disable-next=raise-missing-from
329+
raise ValueError(f'Invalid value {v!r} for "{field}": {exc}')
330+
if type_name == 'list' and elements_checker is not None:
331+
for j, vv in enumerate(v):
332+
try:
333+
v[j] = elements_checker(vv)
334+
except Exception as exc:
335+
# pylint:disable-next=raise-missing-from
336+
raise ValueError(f'Invalid value {vv!r} for "{field}[{i}]": {exc}')
337+
value[i] = v
338+
339+
if not is_list_of_values:
340+
value = value[0]
341+
values[field] = value
342+
343+
274344
class LocalConfig:
275345
"""Settings we want on all of our models."""
276346

@@ -394,11 +464,6 @@ def is_json_value(cls, obj):
394464
raise ValueError('`default` must be a JSON value')
395465
return obj
396466

397-
@p.validator('type', 'elements', pre=True)
398-
# pylint:disable=no-self-argument,no-self-use
399-
def normalize_option_type(cls, obj):
400-
return normalize_option_type_names(obj)
401-
402467
@p.root_validator(pre=True)
403468
# pylint:disable=no-self-argument,no-self-use
404469
def get_rid_of_name(cls, values):
@@ -435,6 +500,18 @@ def merge_typo_names(cls, values):
435500

436501
return values
437502

503+
@p.validator('type', 'elements', pre=True)
504+
# pylint:disable=no-self-argument,no-self-use
505+
def normalize_option_type(cls, obj):
506+
return normalize_option_type_names(obj)
507+
508+
@p.root_validator(pre=True)
509+
# pylint:disable=no-self-argument,no-self-use
510+
def normalize_default_choices(cls, values):
511+
normalize_value(values, 'default')
512+
normalize_value(values, 'choices', is_list_of_values=values.get('type') != 'list')
513+
return values
514+
438515

439516
class SeeAlsoModSchema(BaseModel):
440517
module: str

src/antsibull_docs/schemas/docs/plugin.py

+11-1
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,8 @@
1616
from .base import (REQUIRED_CLI_F, REQUIRED_ENV_VAR_F, RETURN_TYPE_F,
1717
COLLECTION_NAME_F, BaseModel, DeprecationSchema, DocSchema,
1818
LocalConfig, OptionsSchema, list_from_scalars, is_json_value,
19-
normalize_return_type_names, transform_return_docs)
19+
normalize_return_type_names, transform_return_docs,
20+
normalize_value)
2021

2122
_SENTINEL = object()
2223

@@ -123,6 +124,15 @@ def remove_example(cls, values):
123124

124125
return values
125126

127+
@p.root_validator(pre=True)
128+
# pylint:disable=no-self-argument,no-self-use
129+
def normalize_sample(cls, values):
130+
try:
131+
normalize_value(values, 'sample')
132+
except ValueError:
133+
pass
134+
return values
135+
126136

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

0 commit comments

Comments
 (0)