summaryrefslogtreecommitdiff
path: root/test/lib/ansible_test/_util/controller/sanity/validate-modules/validate_modules/schema.py
diff options
context:
space:
mode:
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.py93
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