1"""Application.""" 2import logging 3import os 4from typing import TYPE_CHECKING, Any, List, Type 5 6from ansiblelint import formatters 7from ansiblelint.color import console 8from ansiblelint.errors import MatchError 9 10if TYPE_CHECKING: 11 from argparse import Namespace 12 13 14_logger = logging.getLogger(__package__) 15 16 17class App: 18 """App class represents an execution of the linter.""" 19 20 def __init__(self, options: "Namespace"): 21 """Construct app run based on already loaded configuration.""" 22 options.skip_list = _sanitize_list_options(options.skip_list) 23 options.warn_list = _sanitize_list_options(options.warn_list) 24 25 self.options = options 26 27 formatter_factory = choose_formatter_factory(options) 28 self.formatter = formatter_factory(options.cwd, options.display_relative_path) 29 30 def render_matches(self, matches: List[MatchError]) -> None: 31 """Display given matches.""" 32 if isinstance(self.formatter, formatters.CodeclimateJSONFormatter): 33 # If formatter CodeclimateJSONFormatter is chosen, 34 # then print only the matches in JSON 35 console.print( 36 self.formatter.format_result(matches), markup=False, highlight=False 37 ) 38 return None 39 40 ignored_matches = [match for match in matches if match.ignored] 41 fatal_matches = [match for match in matches if not match.ignored] 42 # Displayed ignored matches first 43 if ignored_matches: 44 _logger.warning( 45 "Listing %s violation(s) marked as ignored, likely already known", 46 len(ignored_matches), 47 ) 48 for match in ignored_matches: 49 if match.ignored: 50 # highlight must be off or apostrophes may produce unexpected results 51 console.print(self.formatter.format(match), highlight=False) 52 if fatal_matches: 53 _logger.warning( 54 "Listing %s violation(s) that are fatal", len(fatal_matches) 55 ) 56 for match in fatal_matches: 57 if not match.ignored: 58 console.print(self.formatter.format(match), highlight=False) 59 60 # If run under GitHub Actions we also want to emit output recognized by it. 61 if os.getenv('GITHUB_ACTIONS') == 'true' and os.getenv('GITHUB_WORKFLOW'): 62 formatter = formatters.AnnotationsFormatter(self.options.cwd, True) 63 for match in matches: 64 console.print(formatter.format(match), markup=False, highlight=False) 65 66 67def choose_formatter_factory( 68 options_list: "Namespace", 69) -> Type[formatters.BaseFormatter[Any]]: 70 """Select an output formatter based on the incoming command line arguments.""" 71 r: Type[formatters.BaseFormatter[Any]] = formatters.Formatter 72 if options_list.format == 'quiet': 73 r = formatters.QuietFormatter 74 elif options_list.parseable_severity: 75 r = formatters.ParseableSeverityFormatter 76 elif options_list.format == 'codeclimate': 77 r = formatters.CodeclimateJSONFormatter 78 elif options_list.parseable or options_list.format == 'pep8': 79 r = formatters.ParseableFormatter 80 return r 81 82 83def _sanitize_list_options(tag_list: List[str]) -> List[str]: 84 """Normalize list options.""" 85 # expand comma separated entries 86 tags = set() 87 for t in tag_list: 88 tags.update(str(t).split(',')) 89 # remove duplicates, and return as sorted list 90 return sorted(set(tags)) 91