Add several new doc<->arg_spec checks (#36247)

* Add several new doc<->arg_spec checks. See #18183

* Update ignore.txt for validate-modules
pull/36470/head
Matt Martz 6 years ago committed by John R Barker
parent 77fa41795e
commit 50adc5409b

@ -110,6 +110,9 @@ Errors
321 ``Exception`` attempting to import module for ``argument_spec`` introspection 321 ``Exception`` attempting to import module for ``argument_spec`` introspection
322 argument is listed in the argument_spec, but not documented in the module 322 argument is listed in the argument_spec, but not documented in the module
323 argument is listed in DOCUMENTATION.options, but not accepted by the module 323 argument is listed in DOCUMENTATION.options, but not accepted by the module
324 Value for "default" from the argument_spec does not match the documentation
325 argument_spec defines type="bool" but documentation does not
326 Value for "choices" from the argument_spec does not match the documentation
.. ..
--------- ------------------- --------- -------------------
**4xx** **Syntax** **4xx** **Syntax**

File diff suppressed because it is too large Load Diff

@ -45,7 +45,7 @@ from module_args import AnsibleModuleImportError, get_argument_spec
from schema import doc_schema, metadata_1_1_schema, return_schema from schema import doc_schema, metadata_1_1_schema, return_schema
from utils import CaptureStd, parse_yaml from utils import CaptureStd, compare_unordered_lists, maybe_convert_bool, parse_yaml
from voluptuous.humanize import humanize_error from voluptuous.humanize import humanize_error
from ansible.module_utils.six import PY3, with_metaclass from ansible.module_utils.six import PY3, with_metaclass
@ -1058,6 +1058,34 @@ class ModuleValidator(Validator):
'should not be marked as required' % arg) 'should not be marked as required' % arg)
) )
doc_default = docs.get('options', {}).get(arg, {}).get('default', None)
if data.get('type') == 'bool':
doc_default = maybe_convert_bool(doc_default)
if 'default' in data and data['default'] != doc_default:
self.reporter.error(
path=self.object_path,
code=324,
msg=('Value for "default" from the argument_spec (%r) for "%s" does not match the '
'documentation (%r)' % (data['default'], arg, doc_default))
)
doc_type = docs.get('options', {}).get(arg, {}).get('type', 'str')
if 'type' in data and data['type'] == 'bool' and doc_type != 'bool':
self.reporter.error(
path=self.object_path,
code=325,
msg='argument_spec for "%s" defines type="bool" but documentation does not' % (arg,)
)
doc_choices = docs.get('options', {}).get(arg, {}).get('choices', [])
if not compare_unordered_lists(data.get('choices', []), doc_choices):
self.reporter.error(
path=self.object_path,
code=326,
msg=('Value for "choices" from the argument_spec (%r) for "%s" does not match the '
'documentation (%r)' % (data.get('choices', []), arg, doc_choices))
)
if docs: if docs:
try: try:
add_fragments(docs, self.object_path, fragment_loader=fragment_loader) add_fragments(docs, self.object_path, fragment_loader=fragment_loader)

@ -25,6 +25,7 @@ import yaml
import yaml.reader import yaml.reader
from ansible.module_utils._text import to_text from ansible.module_utils._text import to_text
from ansible.module_utils.parsing.convert_bool import boolean
class AnsibleTextIOWrapper(TextIOWrapper): class AnsibleTextIOWrapper(TextIOWrapper):
@ -114,3 +115,24 @@ def parse_yaml(value, lineno, module, name, load_all=False):
}) })
return data, errors, traces return data, errors, traces
def maybe_convert_bool(value):
"""Safe conversion to boolean, catching TypeError and returning the original result
Only used in doc<->arg_spec comparisons
"""
try:
return boolean(value)
except TypeError:
return value
def compare_unordered_lists(a, b):
"""Safe list comparisons
Supports:
- unordered lists
- unhashable elements
"""
return len(a) == len(b) and all(x in b for x in a)

Loading…
Cancel
Save