diff options
Diffstat (limited to 'src/ansiblelint/transformer.py')
-rw-r--r-- | src/ansiblelint/transformer.py | 50 |
1 files changed, 40 insertions, 10 deletions
diff --git a/src/ansiblelint/transformer.py b/src/ansiblelint/transformer.py index 3716ef9..c610704 100644 --- a/src/ansiblelint/transformer.py +++ b/src/ansiblelint/transformer.py @@ -1,8 +1,10 @@ +# cspell:ignore classinfo """Transformer implementation.""" + from __future__ import annotations import logging -from typing import TYPE_CHECKING, Union, cast +from typing import TYPE_CHECKING, cast from ruamel.yaml.comments import CommentedMap, CommentedSeq @@ -20,7 +22,6 @@ __all__ = ["Transformer"] _logger = logging.getLogger(__name__) -# pylint: disable=too-few-public-methods class Transformer: """Transformer class marshals transformations. @@ -33,6 +34,17 @@ class Transformer: pre-requisite for the planned rule-specific transforms. """ + DUMP_MSG = "Rewriting yaml file:" + FIX_NA_MSG = "Rule specific fix not available for:" + FIX_NE_MSG = "Rule specific fix not enabled for:" + FIX_APPLY_MSG = "Applying rule specific fix for:" + FIX_FAILED_MSG = "Rule specific fix failed for:" + FIX_ISSUE_MSG = ( + "Please file an issue for this with the task or playbook that caused the error." + ) + FIX_APPLIED_MSG = "Rule specific fix applied for:" + FIX_NOT_APPLIED_MSG = "Rule specific fix not applied for:" + def __init__(self, result: LintResult, options: Options): """Initialize a Transformer instance.""" self.write_set = self.effective_write_set(options.write_list) @@ -44,8 +56,8 @@ class Transformer: self.matches_per_file: dict[Lintable, list[MatchError]] = { file: [] for file in result.files } - - for match in self.matches: + not_ignored = [match for match in self.matches if not match.ignored] + for match in not_ignored: try: lintable = lintables[match.filename] except KeyError: @@ -93,10 +105,13 @@ class Transformer: # We need a fresh YAML() instance for each load because ruamel.yaml # stores intermediate state during load which could affect loading # any other files. (Based on suggestion from ruamel.yaml author) - yaml = FormattedYAML() + yaml = FormattedYAML( + # Ansible only uses YAML 1.1, but others files should use newer 1.2 (ruamel.yaml defaults to 1.2) + version=(1, 1) if file.is_owned_by_ansible() else None, + ) - ruamel_data = yaml.loads(data) - if not isinstance(ruamel_data, (CommentedMap, CommentedSeq)): + ruamel_data = yaml.load(data) + if not isinstance(ruamel_data, CommentedMap | CommentedSeq): # This is an empty vars file or similar which loads as None. # It is not safe to write this file or data-loss is likely. # Only maps and sequences can preserve comments. Skip it. @@ -110,6 +125,7 @@ class Transformer: self._do_transforms(file, ruamel_data or data, file_is_yaml, matches) if file_is_yaml: + _logger.debug("%s %s, version=%s", self.DUMP_MSG, file, yaml.version) # noinspection PyUnboundLocalVariable file.content = yaml.dumps(ruamel_data) @@ -125,17 +141,19 @@ class Transformer: ) -> None: """Do Rule-Transforms handling any last-minute MatchError inspections.""" for match in sorted(matches): + match_id = f"{match.tag}/{match.match_type} {match.filename}:{match.lineno}" if not isinstance(match.rule, TransformMixin): + logging.debug("%s %s", self.FIX_NA_MSG, match_id) continue if self.write_set != {"all"}: rule = cast(AnsibleLintRule, match.rule) rule_definition = set(rule.tags) rule_definition.add(rule.id) if rule_definition.isdisjoint(self.write_set): - # rule transform not requested. Skip it. + logging.debug("%s %s", self.FIX_NE_MSG, match_id) continue if file_is_yaml and not match.yaml_path: - data = cast(Union[CommentedMap, CommentedSeq], data) + data = cast(CommentedMap | CommentedSeq, data) if match.match_type == "play": match.yaml_path = get_path_to_play(file, match.lineno, data) elif match.task or file.kind in ( @@ -144,4 +162,16 @@ class Transformer: "playbook", ): match.yaml_path = get_path_to_task(file, match.lineno, data) - match.rule.transform(match, file, data) + + logging.debug("%s %s", self.FIX_APPLY_MSG, match_id) + try: + match.rule.transform(match, file, data) + except Exception as exc: # pylint: disable=broad-except + _logger.error("%s %s", self.FIX_FAILED_MSG, match_id) # noqa: TRY400 + _logger.exception(exc) # noqa: TRY401 + _logger.error(self.FIX_ISSUE_MSG) # noqa: TRY400 + continue + if match.fixed: + _logger.debug("%s %s", self.FIX_APPLIED_MSG, match_id) + else: + _logger.error("%s %s", self.FIX_NOT_APPLIED_MSG, match_id) |