diff options
Diffstat (limited to 'test/lib/ansible_test/_util/controller/sanity/validate-modules/validate_modules/schema.py')
-rw-r--r-- | test/lib/ansible_test/_util/controller/sanity/validate-modules/validate_modules/schema.py | 93 |
1 files changed, 93 insertions, 0 deletions
diff --git a/test/lib/ansible_test/_util/controller/sanity/validate-modules/validate_modules/schema.py b/test/lib/ansible_test/_util/controller/sanity/validate-modules/validate_modules/schema.py index b979b156e1..9d6614637b 100644 --- a/test/lib/ansible_test/_util/controller/sanity/validate-modules/validate_modules/schema.py +++ b/test/lib/ansible_test/_util/controller/sanity/validate-modules/validate_modules/schema.py @@ -82,10 +82,26 @@ def date(error_code=None): _MODULE = re.compile(r"\bM\(([^)]+)\)") +_PLUGIN = re.compile(r"\bP\(([^)]+)\)") _LINK = re.compile(r"\bL\(([^)]+)\)") _URL = re.compile(r"\bU\(([^)]+)\)") _REF = re.compile(r"\bR\(([^)]+)\)") +_SEM_PARAMETER_STRING = r"\(((?:[^\\)]+|\\.)+)\)" +_SEM_OPTION_NAME = re.compile(r"\bO" + _SEM_PARAMETER_STRING) +_SEM_OPTION_VALUE = re.compile(r"\bV" + _SEM_PARAMETER_STRING) +_SEM_ENV_VARIABLE = re.compile(r"\bE" + _SEM_PARAMETER_STRING) +_SEM_RET_VALUE = re.compile(r"\bRV" + _SEM_PARAMETER_STRING) + +_UNESCAPE = re.compile(r"\\(.)") +_CONTENT_LINK_SPLITTER_RE = re.compile(r'(?:\[[^\]]*\])?\.') +_CONTENT_LINK_END_STUB_RE = re.compile(r'\[[^\]]*\]$') +_FQCN_TYPE_PREFIX_RE = re.compile(r'^([^.]+\.[^.]+\.[^#]+)#([a-z]+):(.*)$') +_IGNORE_MARKER = 'ignore:' +_IGNORE_STRING = '(ignore)' + +_VALID_PLUGIN_TYPES = set(DOCUMENTABLE_PLUGINS) + def _check_module_link(directive, content): if not FULLY_QUALIFIED_COLLECTION_RESOURCE_RE.match(content): @@ -93,6 +109,21 @@ def _check_module_link(directive, content): Invalid('Directive "%s" must contain a FQCN' % directive), 'invalid-documentation-markup') +def _check_plugin_link(directive, content): + if '#' not in content: + raise _add_ansible_error_code( + Invalid('Directive "%s" must contain a "#"' % directive), 'invalid-documentation-markup') + plugin_fqcn, plugin_type = content.split('#', 1) + if not FULLY_QUALIFIED_COLLECTION_RESOURCE_RE.match(plugin_fqcn): + raise _add_ansible_error_code( + Invalid('Directive "%s" must contain a FQCN; found "%s"' % (directive, plugin_fqcn)), + 'invalid-documentation-markup') + if plugin_type not in _VALID_PLUGIN_TYPES: + raise _add_ansible_error_code( + Invalid('Directive "%s" must contain a valid plugin type; found "%s"' % (directive, plugin_type)), + 'invalid-documentation-markup') + + def _check_link(directive, content): if ',' not in content: raise _add_ansible_error_code( @@ -119,6 +150,58 @@ def _check_ref(directive, content): Invalid('Directive "%s" must contain a comma' % directive), 'invalid-documentation-markup') +def _check_sem_quoting(directive, content): + for m in _UNESCAPE.finditer(content): + if m.group(1) not in ('\\', ')'): + raise _add_ansible_error_code( + Invalid('Directive "%s" contains unnecessarily quoted "%s"' % (directive, m.group(1))), + 'invalid-documentation-markup') + return _UNESCAPE.sub(r'\1', content) + + +def _parse_prefix(directive, content): + value = None + if '=' in content: + content, value = content.split('=', 1) + m = _FQCN_TYPE_PREFIX_RE.match(content) + if m: + plugin_fqcn = m.group(1) + plugin_type = m.group(2) + content = m.group(3) + if not FULLY_QUALIFIED_COLLECTION_RESOURCE_RE.match(plugin_fqcn): + raise _add_ansible_error_code( + Invalid('Directive "%s" must contain a FQCN; found "%s"' % (directive, plugin_fqcn)), + 'invalid-documentation-markup') + if plugin_type not in _VALID_PLUGIN_TYPES: + raise _add_ansible_error_code( + Invalid('Directive "%s" must contain a valid plugin type; found "%s"' % (directive, plugin_type)), + 'invalid-documentation-markup') + elif content.startswith(_IGNORE_MARKER): + content = content[len(_IGNORE_MARKER):] + plugin_fqcn = plugin_type = _IGNORE_STRING + else: + plugin_fqcn = plugin_type = None + if ':' in content or '#' in content: + raise _add_ansible_error_code( + Invalid('Directive "%s" contains wrongly specified FQCN/plugin type' % directive), + 'invalid-documentation-markup') + content_link = _CONTENT_LINK_SPLITTER_RE.split(content) + for i, part in enumerate(content_link): + if i == len(content_link) - 1: + part = _CONTENT_LINK_END_STUB_RE.sub('', part) + content_link[i] = part + if '.' in part or '[' in part or ']' in part: + raise _add_ansible_error_code( + Invalid('Directive "%s" contains invalid name "%s"' % (directive, content)), + 'invalid-documentation-markup') + return plugin_fqcn, plugin_type, content_link, content, value + + +def _check_sem_option_return_value(directive, content): + content = _check_sem_quoting(directive, content) + _parse_prefix(directive, content) + + def doc_string(v): """Match a documentation string.""" if not isinstance(v, string_types): @@ -126,12 +209,22 @@ def doc_string(v): Invalid('Must be a string'), 'invalid-documentation') for m in _MODULE.finditer(v): _check_module_link(m.group(0), m.group(1)) + for m in _PLUGIN.finditer(v): + _check_plugin_link(m.group(0), m.group(1)) for m in _LINK.finditer(v): _check_link(m.group(0), m.group(1)) for m in _URL.finditer(v): _check_url(m.group(0), m.group(1)) for m in _REF.finditer(v): _check_ref(m.group(0), m.group(1)) + for m in _SEM_OPTION_NAME.finditer(v): + _check_sem_option_return_value(m.group(0), m.group(1)) + for m in _SEM_OPTION_VALUE.finditer(v): + _check_sem_quoting(m.group(0), m.group(1)) + for m in _SEM_ENV_VARIABLE.finditer(v): + _check_sem_quoting(m.group(0), m.group(1)) + for m in _SEM_RET_VALUE.finditer(v): + _check_sem_option_return_value(m.group(0), m.group(1)) return v |