ansible-doctor/ansibledoctor/doc_parser.py

146 lines
5.9 KiB
Python
Raw Normal View History

2019-10-07 06:52:00 +00:00
#!/usr/bin/env python3
2019-10-08 09:39:27 +00:00
"""Parse static files."""
2019-10-07 06:52:00 +00:00
import fnmatch
from collections import defaultdict
from contextlib import suppress
2019-10-07 06:52:00 +00:00
import anyconfig
import ruamel.yaml
from nested_lookup import nested_lookup
2019-10-07 06:52:00 +00:00
from ansibledoctor.annotation import Annotation
from ansibledoctor.config import SingleConfig
from ansibledoctor.contstants import YAML_EXTENSIONS
from ansibledoctor.file_registry import Registry
from ansibledoctor.utils import SingleLog, UnsafeTag, flatten
2019-10-07 06:52:00 +00:00
class Parser:
2020-04-05 21:16:53 +00:00
"""Parse yaml files."""
2019-10-07 06:52:00 +00:00
def __init__(self):
self._annotation_objs = {}
self._data = defaultdict(dict)
self.config = SingleConfig()
self.log = SingleLog()
self.logger = SingleLog().logger
2019-10-07 06:52:00 +00:00
self._files_registry = Registry()
self._parse_meta_file()
self._parse_var_files()
self._parse_task_tags()
2019-10-07 06:52:00 +00:00
self._populate_doc_data()
def _yaml_remove_comments(self, d):
if isinstance(d, dict):
for k, v in d.items():
self._yaml_remove_comments(k)
self._yaml_remove_comments(v)
elif isinstance(d, list):
for elem in d:
self._yaml_remove_comments(elem)
with suppress(AttributeError):
attr = (
"comment"
if isinstance(d, ruamel.yaml.scalarstring.ScalarString)
else ruamel.yaml.comments.Comment.attrib
)
delattr(d, attr)
def _parse_var_files(self):
2019-10-07 06:52:00 +00:00
for rfile in self._files_registry.get_files():
if any(fnmatch.fnmatch(rfile, "*/defaults/*." + ext) for ext in YAML_EXTENSIONS):
with open(rfile, encoding="utf8") as yaml_file:
2019-10-07 06:52:00 +00:00
try:
2020-04-05 21:16:53 +00:00
ruamel.yaml.add_constructor(
UnsafeTag.yaml_tag,
UnsafeTag.yaml_constructor,
constructor=ruamel.yaml.SafeConstructor,
2020-04-05 21:16:53 +00:00
)
raw = ruamel.yaml.YAML(typ="rt").load(yaml_file)
self._yaml_remove_comments(raw)
data = defaultdict(dict, raw or {})
2019-10-07 06:52:00 +00:00
for key, value in data.items():
self._data["var"][key] = {"value": {key: value}}
2020-04-05 21:16:53 +00:00
except (
ruamel.yaml.composer.ComposerError,
ruamel.yaml.scanner.ScannerError,
ruamel.yaml.constructor.ConstructorError,
ruamel.yaml.constructor.DuplicateKeyError,
2020-04-05 21:16:53 +00:00
) as e:
message = f"{e.context} {e.problem}"
2020-04-05 21:16:53 +00:00
self.log.sysexit_with_message(
f"Unable to read yaml file {rfile}\n{message}"
2020-04-05 21:16:53 +00:00
)
2019-10-07 06:52:00 +00:00
def _parse_meta_file(self):
self._data["meta"]["name"] = {"value": self.config.config["role_name"]}
2019-10-07 06:52:00 +00:00
for rfile in self._files_registry.get_files():
if any("meta/main." + ext in rfile for ext in YAML_EXTENSIONS):
with open(rfile, encoding="utf8") as yaml_file:
2019-10-07 06:52:00 +00:00
try:
raw = ruamel.yaml.YAML(typ="rt").load(yaml_file)
self._yaml_remove_comments(raw)
data = defaultdict(dict, raw)
2019-10-07 06:52:00 +00:00
if data.get("galaxy_info"):
for key, value in data.get("galaxy_info").items():
self._data["meta"][key] = {"value": value}
2019-10-08 09:30:31 +00:00
if data.get("dependencies") is not None:
2020-04-05 21:16:53 +00:00
self._data["meta"]["dependencies"] = {
"value": data.get("dependencies")
}
except (
ruamel.yaml.composer.ComposerError,
ruamel.yaml.scanner.ScannerError,
2020-04-05 21:16:53 +00:00
) as e:
message = f"{e.context} {e.problem}"
2020-04-05 21:16:53 +00:00
self.log.sysexit_with_message(
f"Unable to read yaml file {rfile}\n{message}"
2020-04-05 21:16:53 +00:00
)
2019-11-07 08:13:39 +00:00
def _parse_task_tags(self):
for rfile in self._files_registry.get_files():
if any(fnmatch.fnmatch(rfile, "*/tasks/*." + ext) for ext in YAML_EXTENSIONS):
with open(rfile, encoding="utf8") as yaml_file:
try:
raw = ruamel.yaml.YAML(typ="rt").load(yaml_file)
self._yaml_remove_comments(raw)
tags = list(set(flatten(nested_lookup("tags", raw))))
for tag in [
x for x in tags if x not in self.config.config["exclude_tags"]
]:
self._data["tag"][tag] = {"value": tag}
2020-04-05 21:16:53 +00:00
except (
ruamel.yaml.composer.ComposerError,
ruamel.yaml.scanner.ScannerError,
2020-04-05 21:16:53 +00:00
) as e:
message = f"{e.context} {e.problem}"
2020-04-05 21:16:53 +00:00
self.log.sysexit_with_message(
f"Unable to read yaml file {rfile}\n{message}"
2020-04-05 21:16:53 +00:00
)
2019-10-07 06:52:00 +00:00
def _populate_doc_data(self):
"""Generate the documentation data object."""
tags = defaultdict(dict)
for annotation in self.config.get_annotations_names(automatic=True):
self.logger.info(f"Finding annotations for: @{annotation}")
self._annotation_objs[annotation] = Annotation(
name=annotation, files_registry=self._files_registry
2020-04-05 21:16:53 +00:00
)
tags[annotation] = self._annotation_objs[annotation].get_details()
try:
anyconfig.merge(self._data, tags, ac_merge=anyconfig.MS_DICTS)
except ValueError as e:
self.log.sysexit_with_message(f"Unable to merge annotation values:\n{e}")
2019-10-07 06:52:00 +00:00
def get_data(self):
return self._data