diff --git a/yamllint/cli.py b/yamllint/cli.py index 93cecc5..617064a 100644 --- a/yamllint/cli.py +++ b/yamllint/cli.py @@ -118,7 +118,7 @@ def run(argv=None): except EnvironmentError as e: print(e, file=sys.stderr) sys.exit(-1) - all_problems[file] = problems + all_problems[file] = [pb for pb in problems if pb] if args.stdin: # read yaml from stdin @@ -127,7 +127,7 @@ def run(argv=None): except EnvironmentError as e: print(e, file=sys.stderr) sys.exit(-1) - all_problems['stdin'] = problems + all_problems['stdin'] = [pb for pb in problems if pb] max_level = show_all_problems( all_problems, diff --git a/yamllint/format.py b/yamllint/format.py index e98103a..4ddcc4c 100644 --- a/yamllint/format.py +++ b/yamllint/format.py @@ -17,29 +17,149 @@ def supports_color(): hasattr(sys.stdout, 'isatty') and sys.stdout.isatty()) -class Format(object): - @staticmethod - def parsable(problem, filename): - return ('%(file)s:%(line)s:%(column)s: [%(level)s] %(message)s' % - {'file': filename, - 'line': problem.line, - 'column': problem.column, - 'level': problem.level, - 'message': problem.message}) - - @staticmethod - def standard(problem, filename): - line = ' %d:%d' % (problem.line, problem.column) - line += max(12 - len(line), 0) * ' ' +def run_on_gh(): + """Return if the currnet job is on github.""" + return 'GITHUB_ACTIONS' in os.environ and 'GITHUB_WORKFLOW' in os.environ + + +class Formater(object): + """Any formater.""" + # the formater name + name = '' + + @classmethod + def get_formater(cls, name, no_warn): + """Return a formater instance.""" + + if name == 'auto': + if run_on_gh(): + name = 'github' + elif supports_color(): + name = 'colored' + else: + name = 'standard' + + for formater in cls.__subclasses__(): + if name == formater.name: + return formater(no_warn) + raise ValueError('unknown formater: %s' % name) + + def __init__(self, no_warn): + """Setup the formater.""" + self.no_warn = no_warn + + def show_problems_for_all_files(self, all_problems): + """Show all problems of all files.""" + raise NotImplementedError() + + def show_problems_for_file(self, problems, file): + """Show all problems of a specific file.""" + raise NotImplementedError() + + def show_problem(self, problem, file): + """Show all problems of a specific file.""" + raise NotImplementedError() + + +class ParsableFormater(Formater): + """The parsable formater.""" + name = 'parsable' + + def show_problems_for_all_files(self, all_problems): + """Show all problems of all files.""" + string = '' + for file, problems in all_problems.items(): + string += self.show_problems_for_file(problems, file) + return string + + def show_problems_for_file(self, problems, file): + """Show all problems of a specific file.""" + string = '' + for problem in problems: + string += self.show_problem(problem, file) + return string + + def show_problem(self, problem, file): + """Show all problems of a specific file.""" + if self.no_warn and (problem.level != 'error'): + return '' + return ( + '%(file)s:%(line)s:%(column)s: [%(level)s] %(message)s\n' % + { + 'file': file, + 'line': problem.line, + 'column': problem.column, + 'level': problem.level, + 'message': problem.message + } + ) + + +class GithubFormater(Formater): + """The parsable formater.""" + name = 'github' + + def show_problems_for_all_files(self, all_problems): + """Show all problems of all files.""" + string = '' + for file, problems in all_problems.items(): + string += self.show_problems_for_file(problems, file) + return string + + def show_problems_for_file(self, problems, file): + """Show all problems of a specific file.""" + string = '::group::%s\n' % file + for problem in problems: + string += self.show_problem(problem, file) + if string == '::group::%s\n' % file: + return '' + return string + '::endgroup::\n\n' + + def show_problem(self, problem, file): + """Show all problems of a specific file.""" + if self.no_warn and (problem.level != 'error'): + return '' + line = '::' line += problem.level - line += max(21 - len(line), 0) * ' ' - line += problem.desc + line += ' file=' + file + ',' + line += 'line=' + format(problem.line) + ',' + line += 'col=' + format(problem.column) + line += '::' + line += format(problem.line) + line += ':' + line += format(problem.column) + line += ' ' if problem.rule: - line += ' (%s)' % problem.rule + line += '[' + problem.rule + '] ' + line += problem.desc + line += '\n' return line - @staticmethod - def standard_color(problem, filename): + +class ColoredFormater(Formater): + """The parsable formater.""" + name = 'colored' + + def show_problems_for_all_files(self, all_problems): + """Show all problems of all files.""" + string = '' + for file, problems in all_problems.items(): + string += self.show_problems_for_file(problems, file) + return string + + def show_problems_for_file(self, problems, file): + """Show all problems of a specific file.""" + string = '\033[4m%s\033[0m\n' % file + for problem in problems: + string += self.show_problem(problem, file) + if string == '\033[4m%s\033[0m\n' % file: + return '' + return string + '\n' + + def show_problem(self, problem, file): + """Show all problems of a specific file.""" + if self.no_warn and (problem.level != 'error'): + return '' line = ' \033[2m%d:%d\033[0m' % (problem.line, problem.column) line += max(20 - len(line), 0) * ' ' if problem.level == 'warning': @@ -50,73 +170,58 @@ class Format(object): line += problem.desc if problem.rule: line += ' \033[2m(%s)\033[0m' % problem.rule + line += '\n' return line - @staticmethod - def github(problem, filename): - line = '::' + +class StandardFormater(Formater): + """The parsable formater.""" + name = 'standard' + + def show_problems_for_all_files(self, all_problems): + """Show all problems of all files.""" + string = '' + for file, problems in all_problems.items(): + string += self.show_problems_for_file(problems, file) + return string + + def show_problems_for_file(self, problems, file): + """Show all problems of a specific file.""" + string = file + '\n' + for problem in problems: + string += self.show_problem(problem, file) + if string == file + '\n': + return '' + return string + '\n' + + def show_problem(self, problem, file): + """Show all problems of a specific file.""" + if self.no_warn and (problem.level != 'error'): + return '' + line = ' %d:%d' % (problem.line, problem.column) + line += max(12 - len(line), 0) * ' ' line += problem.level - line += ' file=' + filename + ',' - line += 'line=' + format(problem.line) + ',' - line += 'col=' + format(problem.column) - line += '::' - line += format(problem.line) - line += ':' - line += format(problem.column) - line += ' ' - if problem.rule: - line += '[' + problem.rule + '] ' + line += max(21 - len(line), 0) * ' ' line += problem.desc + if problem.rule: + line += ' (%s)' % problem.rule + line += '\n' return line -def show_problems(problems, file, args_format, no_warn): - first = True - - if args_format == 'auto': - if ('GITHUB_ACTIONS' in os.environ and - 'GITHUB_WORKFLOW' in os.environ): - args_format = 'github' - elif supports_color(): - args_format = 'colored' - - for problem in problems: - if no_warn and (problem.level != 'error'): - continue - if args_format == 'parsable': - print(Format.parsable(problem, file)) - elif args_format == 'github': - if first: - print('::group::%s' % file) - first = False - print(Format.github(problem, file)) - elif args_format == 'colored': - if first: - print('\033[4m%s\033[0m' % file) - first = False - print(Format.standard_color(problem, file)) - else: - if first: - print(file) - first = False - print(Format.standard(problem, file)) - - if not first and args_format == 'github': - print('::endgroup::') - - if not first and args_format != 'parsable': - print('') - - -def max_level(problems): +def max_level(all_problems): """Return the max level of all problems.""" - return max(PROBLEM_LEVELS[problem.level] for problem in problems) + all_levels = [problem.level for problems in all_problems.values() for problem in problems] + if all_levels: + return max(map(lambda x: PROBLEM_LEVELS[x], all_levels)) + return 0 def show_all_problems(all_problems, args_format, no_warn): """Print all problems, return the max level.""" - for file, problem in all_problems.items(): - show_problems(problem, file, args_format, no_warn) + fmt = Formater.get_formater(args_format, no_warn) + + print(fmt.show_problems_for_all_files(all_problems), end='') return max_level(all_problems)