validate-modules: reject option/alias names equal up to casing belonging to different options (#83530)

* Reject option/alias names equal up to casing belonging to different options.

* Update test/lib/ansible_test/_util/controller/sanity/validate-modules/validate_modules/main.py

Co-authored-by: Sloane Hertel <19572925+s-hertel@users.noreply.github.com>
pull/83539/head
Felix Fontein 5 months ago committed by GitHub
parent 63538f7779
commit e5309ba29f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -0,0 +1,2 @@
minor_changes:
- "validate-modules sanity test - reject option/aliases names that are identical up to casing but belong to different options (https://github.com/ansible/ansible/pull/83530)."

@ -0,0 +1,45 @@
#!/usr/bin/python
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
from __future__ import annotations
DOCUMENTATION = '''
module: option_name_casing
short_description: Option names equal up to casing
description: Option names equal up to casing.
author:
- Ansible Core Team
options:
foo:
description: Foo
type: str
aliases:
- bar
- FOO # this one is ok
Foo:
description: Foo alias
type: str
Bar:
description: Bar alias
type: str
bam:
description: Bar alias 2
aliases:
- baR
type: str
'''
EXAMPLES = '''#'''
RETURN = ''''''
from ansible.module_utils.basic import AnsibleModule
if __name__ == '__main__':
module = AnsibleModule(argument_spec=dict(
foo=dict(type='str', aliases=['bar', 'FOO']),
Foo=dict(type='str'),
Bar=dict(type='str'),
bam=dict(type='str', aliases=['baR'])
))
module.exit_json()

@ -12,6 +12,8 @@ plugins/modules/invalid_yaml_syntax.py:0:0: missing-documentation: No DOCUMENTAT
plugins/modules/invalid_yaml_syntax.py:7:15: documentation-syntax-error: DOCUMENTATION is not valid YAML
plugins/modules/invalid_yaml_syntax.py:11:15: invalid-examples: EXAMPLES is not valid YAML
plugins/modules/invalid_yaml_syntax.py:15:15: return-syntax-error: RETURN is not valid YAML
plugins/modules/option_name_casing.py:0:0: option-equal-up-to-casing: Multiple options/aliases are equal up to casing: option 'Bar', alias 'baR' of option 'bam', alias 'bar' of option 'foo'
plugins/modules/option_name_casing.py:0:0: option-equal-up-to-casing: Multiple options/aliases are equal up to casing: option 'Foo', option 'foo'
plugins/modules/semantic_markup.py:0:0: invalid-documentation-markup: DOCUMENTATION.options.a11.suboptions.b1.description.0: While parsing "V(C\(" at index 1: Unnecessarily escaped "(" @ data['options']['a11']['suboptions']['b1']['description'][0]. Got 'V(C\\(foo\\)).'
plugins/modules/semantic_markup.py:0:0: invalid-documentation-markup: DOCUMENTATION.options.a11.suboptions.b1.description.2: While parsing "P(foo.bar#baz)" at index 1: Plugin name "foo.bar" is not a FQCN @ data['options']['a11']['suboptions']['b1']['description'][2]. Got 'P(foo.bar#baz).'
plugins/modules/semantic_markup.py:0:0: invalid-documentation-markup: DOCUMENTATION.options.a11.suboptions.b1.description.3: While parsing "P(foo.bar.baz)" at index 1: Parameter "foo.bar.baz" is not of the form FQCN#type @ data['options']['a11']['suboptions']['b1']['description'][3]. Got 'P(foo.bar.baz).'

@ -838,6 +838,46 @@ class ModuleValidator(Validator):
msg='%s: %s' % (combined_path, error_message)
)
def _validate_option_docs(self, options, context=None):
if not isinstance(options, dict):
return
if context is None:
context = []
normalized_option_alias_names = dict()
def add_option_alias_name(name, option_name):
normalized_name = str(name).lower()
normalized_option_alias_names.setdefault(normalized_name, {}).setdefault(option_name, set()).add(name)
for option, data in options.items():
if 'suboptions' in data:
self._validate_option_docs(data.get('suboptions'), context + [option])
add_option_alias_name(option, option)
if 'aliases' in data and isinstance(data['aliases'], list):
for alias in data['aliases']:
add_option_alias_name(alias, option)
for normalized_name, options in normalized_option_alias_names.items():
if len(options) < 2:
continue
what = []
for option_name, names in sorted(options.items()):
if option_name in names:
what.append("option '%s'" % option_name)
else:
what.append("alias '%s' of option '%s'" % (sorted(names)[0], option_name))
msg = "Multiple options/aliases"
if context:
msg += " found in %s" % " -> ".join(context)
msg += " are equal up to casing: %s" % ", ".join(what)
self.reporter.error(
path=self.object_path,
code='option-equal-up-to-casing',
msg=msg,
)
def _validate_docs(self):
doc = None
# We have three ways of marking deprecated/removed files. Have to check each one
@ -1015,6 +1055,9 @@ class ModuleValidator(Validator):
'invalid-documentation',
)
if doc:
self._validate_option_docs(doc.get('options'))
self._validate_all_semantic_markup(doc, returns)
if not self.collection:

Loading…
Cancel
Save