def execute(self, finder): issues = [] rules = [ rule for rule, _ in self.get_all_codes() if rule not in self.config['disabled'] ] linter = Linter(self.config['options']['variable-formats'], rules) tmpl_linter = TemplateLinter( self.config['options']['variable-formats'], rules, ) for filepath in finder.files(self.config['filters']): try: file_content = finder.read_file(filepath) except Exception as exc: # pylint: disable=broad-except issues.append(self.make_issue(exc, filepath)) continue try: if filepath.endswith('.po'): errors = linter.verify_file(file_content) elif filepath.endswith('.pot'): errors = tmpl_linter.verify_file(file_content) except IOError as exc: issues.append(ParseIssue(exc, filepath)) continue except Exception as exc: # pylint: disable=broad-except issues.append(self.make_issue(exc, filepath)) continue issues += [self.make_issue(error, filepath) for error in errors] return issues
def test_linter(self): pofile = build_po_string('#: foo/foo.py:5\n' 'msgid "Foo"\n' 'msgstr "Oof"\n') linter = Linter(['python-format', 'python-brace-format'], ['E201', 'W202']) msgs = linter.verify_file(pofile) # No warnings or errors, so there are no messages. assert len(msgs) == 0
def test_linter(self): pofile = build_po_string("#: foo/foo.py:5\n" 'msgid "Foo"\n' 'msgstr "Oof"\n') linter = Linter(["python-format", "python-brace-format"], ["E201", "W202"]) msgs = linter.verify_file(pofile) # No warnings or errors, so there are no messages. assert len(msgs) == 0
def test_complex(self): data = build_po_string( '#: foo/foo.py:5\n' 'msgid "Foo: {bar} {baz}"\n' 'msgstr "Oof: {foo} {bar}"\n') linter = Linter(['python']) results = linter.verify_file(data) eq_(len(results), 1) eq_(results[0].missing, [u'{baz}']) eq_(results[0].invalid, [u'{foo}'])
def test_linter_untranslated_strings(self): pofile = build_po_string('#, fuzzy\n' '#~ msgid "Most Recent Message"\n' '#~ msgid_plural "Last %(count)s Messages"\n' '#~ msgstr[0] ""\n' '#~ msgstr[1] ""\n') linter = Linter(['python-format', 'python-brace-format'], ['E201', 'W202']) msgs = linter.verify_file(pofile) # There were no translated strings, so nothing to lint. assert len(msgs) == 0
def test_linter_untranslated_strings(self): pofile = build_po_string( '#, fuzzy\n' '#~ msgid "Most Recent Message"\n' '#~ msgid_plural "Last %(count)s Messages"\n' '#~ msgstr[0] ""\n' '#~ msgstr[1] ""\n') linter = Linter(['pysprintf', 'pyformat'], ['E201', 'W202']) results = linter.verify_file(pofile) # There were no translated strings, so nothing to lint. eq_(len(results), 0)
def test_linter_fuzzy_strings(self): pofile = build_po_string( '#, fuzzy\n' '#~ msgid "Most Recent Message"\n' '#~ msgid_plural "Last %(count)s Messages"\n' '#~ msgstr[0] "Les {count} derniers messages"\n' '#~ msgstr[1] "Les {count} derniers messages"\n') linter = Linter(['python'], ['mismatched']) results = linter.verify_file(pofile) # There were no non-fuzzy strings, so nothing to lint. eq_(len(results), 0)
def test_linter_fuzzy_strings(self): pofile = build_po_string( "#, fuzzy\n" '#~ msgid "Most Recent Message"\n' '#~ msgid_plural "Last %(count)s Messages"\n' '#~ msgstr[0] "Les {count} derniers messages"\n' '#~ msgstr[1] "Les {count} derniers messages"\n') linter = Linter(["python-format", "python-brace-format"], ["E201", "W202"]) msgs = linter.verify_file(pofile) # There were no non-fuzzy strings, so nothing to lint. assert len(msgs) == 0
def test_linter(self): pofile = build_po_string( '#: foo/foo.py:5\n' 'msgid "Foo"\n' 'msgstr "Oof"\n') linter = Linter( ['python-format', 'python-brace-format'], ['E201', 'W202'] ) msgs = linter.verify_file(pofile) # No warnings or errors, so there are no messages. assert len(msgs) == 0
def test_linter(self): pofile = build_po_string( '#: foo/foo.py:5\n' 'msgid "Foo"\n' 'msgstr "Oof"\n') linter = Linter(['pysprintf', 'pyformat'], ['E201', 'W202']) results = linter.verify_file(pofile) # This should give us one linted entry with no errors # and no warnings in it. eq_(len(results), 1) eq_(len(results[0].errors), 0) eq_(len(results[0].warnings), 0)
def test_linter_untranslated_strings(self): pofile = build_po_string( '#, fuzzy\n' '#~ msgid "Most Recent Message"\n' '#~ msgid_plural "Last %(count)s Messages"\n' '#~ msgstr[0] ""\n' '#~ msgstr[1] ""\n') linter = Linter( ['python-format', 'python-brace-format'], ['E201', 'W202'] ) msgs = linter.verify_file(pofile) # There were no translated strings, so nothing to lint. assert len(msgs) == 0
def test_linter_fuzzy_strings(self): pofile = build_po_string( '#, fuzzy\n' '#~ msgid "Most Recent Message"\n' '#~ msgid_plural "Last %(count)s Messages"\n' '#~ msgstr[0] "Les {count} derniers messages"\n' '#~ msgstr[1] "Les {count} derniers messages"\n') linter = Linter( ['python-format', 'python-brace-format'], ['E201', 'W202'] ) msgs = linter.verify_file(pofile) # There were no non-fuzzy strings, so nothing to lint. eq_(len(msgs), 0)
def test_fine(self): po_string = build_po_string( '#: foo/foo.py:5\n' 'msgid "Foo"\n' 'msgstr "Oof"\n') linter = Linter(['python']) results = linter.verify_file(po_string) eq_(len(results), 1) eq_(results[0].missing, []) eq_(results[0].invalid, []) po_string = build_po_string( '#: foo/foo.py:5\n' 'msgid "Foo: {foo}"\n' 'msgstr "Oof: {foo}"\n') linter = Linter(['python']) results = linter.verify_file(po_string) eq_(len(results), 1) eq_(results[0].missing, []) eq_(results[0].invalid, [])
def lint(ctx, quiet, color, varformat, rules, excluderules, reporter, errorsonly, path): """ Lints .po/.pot files for issues You can ignore rules on a string-by-string basis by adding an extracted comment "dennis-ignore: <comma-separated-rules>". See documentation for details. """ global TERM if not quiet: out("dennis version {version}".format(version=__version__)) if not color: TERM = FauxTerminal() # Make sure requested rules are valid all_rules = get_linter_rules(with_names=True) all_rules.update(get_template_linter_rules(with_names=True)) rules = [rule.strip() for rule in rules.split(",") if rule.strip()] invalid_rules = [rule for rule in rules if rule not in all_rules] if invalid_rules: raise click.UsageError("invalid rules: %s." % ", ".join(invalid_rules)) if not rules: rules = get_linter_rules() rules.update(get_template_linter_rules()) rules = rules.keys() if excluderules: excludes = [ rule.strip() for rule in excluderules.split(",") if rule.strip() ] invalid_rules = [rule for rule in excludes if rule not in all_rules] if invalid_rules: raise click.UsageError("invalid exclude rules: %s." % ", ".join(invalid_rules)) # Remove excluded rules rules = [rule for rule in rules if rule not in excludes] # Build linters and lint linter = Linter(varformat.split(","), rules) templatelinter = TemplateLinter(varformat.split(","), rules) po_files = [] for item in path: if os.path.isdir(item): for root, dirs, files in os.walk(item): po_files.extend([ os.path.join(root, fn) for fn in files if fn.endswith((".po", ".pot")) ]) else: po_files.append(item) po_files = [ os.path.abspath(fn) for fn in po_files if fn.endswith((".po", ".pot")) ] if not po_files: raise click.UsageError("nothing to work on. Use --help for help.") files_to_errors = {} total_error_count = 0 total_warning_count = 0 total_files_with_errors = 0 for fn in po_files: try: if not os.path.exists(fn): raise click.UsageError('File "{fn}" does not exist.'.format( fn=click.format_filename(fn))) if fn.endswith(".po"): results = linter.verify_file(fn) else: results = templatelinter.verify_file(fn) except IOError as ioe: # This is not a valid .po file. So mark it as an error. err(">>> Problem opening file: {fn}".format( fn=click.format_filename(fn))) err(repr(ioe)) out("") # FIXME - should we track this separately as an invalid # file? files_to_errors[fn] = (1, 0) total_error_count += 1 continue if errorsonly: # Go through and nix all the non-error LintMessages results = [res for res in results if res.kind == "err"] # We don't want to print output for files that are fine, so we # update the bookkeeping and move on. if not results: files_to_errors[fn] = (0, 0) continue if not quiet and not reporter: out( TERM.bold_green, ">>> Working on: {fn}".format(fn=click.format_filename(fn)), TERM.normal, ) error_results = [res for res in results if res.kind == "err"] warning_results = [res for res in results if res.kind == "warn"] error_count = len(error_results) total_error_count += error_count warning_count = len(warning_results) total_warning_count += warning_count if not quiet: for msg in error_results: if reporter == "line": out( fn, ":", str(msg.poentry.linenum), ":", "0", ":", msg.code, ":", msg.msg, ) else: out(TERM.bold_red, msg.code, ": ", msg.msg, TERM.normal) out(withlines(msg.poentry.linenum, msg.poentry.original)) out("") if not quiet and not errorsonly: for msg in warning_results: if reporter == "line": out( fn, ":", str(msg.poentry.linenum), ":", "0", ":", msg.code, ":", msg.msg, ) else: out(TERM.bold_yellow, msg.code, ": ", msg.msg, TERM.normal) out(withlines(msg.poentry.linenum, msg.poentry.original)) out("") files_to_errors[fn] = (error_count, warning_count) if error_count > 0: total_files_with_errors += 1 if not quiet and reporter != "line": out("Totals") if not errorsonly: out(" Warnings: {warnings:5}".format(warnings=warning_count)) out(" Errors: {errors:5}\n".format(errors=error_count)) if len(po_files) > 1 and not quiet and reporter != "line": out("Final totals") out(" Number of files examined: {count:5}".format( count=len(po_files))) out(" Total number of files with errors: {count:5}".format( count=total_files_with_errors)) if not errorsonly: out(" Total number of warnings: {count:5}".format( count=total_warning_count)) out(" Total number of errors: {count:5}".format( count=total_error_count)) out("") file_counts = [(counts[0], counts[1], fn.split(os.sep)[-3], fn.split(os.sep)[-1]) for (fn, counts) in files_to_errors.items()] # If we're showing errors only, then don't talk about warnings. if errorsonly: header = "Errors Filename" line = " {errors:5} {locale} ({fn})" else: header = "Warnings Errors Filename" line = " {warnings:5} {errors:5} {locale} ({fn})" file_counts = list(reversed(sorted(file_counts))) printed_header = False for error_count, warning_count, locale, fn in file_counts: if not error_count and not warning_count: continue if not printed_header: out(header) printed_header = True out( line.format(warnings=warning_count, errors=error_count, fn=fn, locale=locale)) # Return 0 if everything was fine or 1 if there were errors. ctx.exit(code=1 if total_error_count else 0)
def lint(ctx, quiet, color, varformat, rules, reporter, errorsonly, path): """ Lints .po/.pot files for issues You can ignore rules on a string-by-string basis by adding an extracted comment "dennis-ignore: <comma-separated-rules>". See documentation for details. """ global TERM if not quiet: out('dennis version {version}'.format(version=__version__)) if not color: TERM = FauxTerminal() linter = Linter(varformat.split(','), rules.split(',')) templatelinter = TemplateLinter(varformat.split(','), rules.split(',')) po_files = [] for item in path: if os.path.isdir(item): for root, dirs, files in os.walk(item): po_files.extend( [os.path.join(root, fn) for fn in files if fn.endswith(('.po', '.pot'))]) else: po_files.append(item) po_files = [os.path.abspath(fn) for fn in po_files if fn.endswith(('.po', '.pot'))] if not po_files: err('Nothing to work on. Use --help for help.') ctx.exit(1) files_to_errors = {} total_error_count = 0 total_warning_count = 0 total_files_with_errors = 0 for fn in po_files: try: if not os.path.exists(fn): raise IOError('File "{fn}" does not exist.'.format(fn=fn)) if fn.endswith('.po'): results = linter.verify_file(fn) else: results = templatelinter.verify_file(fn) except IOError as ioe: # This is not a valid .po file. So mark it as an error. err('>>> Problem opening file: {fn}'.format(fn=fn)) err(repr(ioe)) out('') # FIXME - should we track this separately as an invalid # file? files_to_errors[fn] = (1, 0) total_error_count += 1 continue if errorsonly: # Go through and nix all the non-error LintMessages results = [res for res in results if res.kind == 'err'] # We don't want to print output for files that are fine, so we # update the bookkeeping and move on. if not results: files_to_errors[fn] = (0, 0) continue if not quiet and not reporter: out(TERM.bold_green, '>>> Working on: {fn}'.format(fn=fn), TERM.normal) error_results = [res for res in results if res.kind == 'err'] warning_results = [res for res in results if res.kind == 'warn'] error_count = len(error_results) total_error_count += error_count warning_count = len(warning_results) total_warning_count += warning_count if not quiet: for msg in error_results: if reporter == 'line': out(fn, ':', textclass(msg.poentry.linenum), ':', '0', ':', msg.code, ':', msg.msg) else: out(TERM.bold_red, msg.code, ': ', msg.msg, TERM.normal) out(withlines(msg.poentry.linenum, msg.poentry.original)) out('') if not quiet and not errorsonly: for msg in warning_results: if reporter == 'line': out(fn, ':', textclass(msg.poentry.linenum), ':', '0', ':', msg.code, ':', msg.msg) else: out(TERM.bold_yellow, msg.code, ': ', msg.msg, TERM.normal) out(withlines(msg.poentry.linenum, msg.poentry.original)) out('') files_to_errors[fn] = (error_count, warning_count) if error_count > 0: total_files_with_errors += 1 if not quiet and reporter != 'line': out('Totals') if not errorsonly: out(' Warnings: {warnings:5}'.format(warnings=warning_count)) out(' Errors: {errors:5}\n'.format(errors=error_count)) if len(po_files) > 1 and not quiet and reporter != 'line': out('Final totals') out(' Number of files examined: {count:5}'.format( count=len(po_files))) out(' Total number of files with errors: {count:5}'.format( count=total_files_with_errors)) if not errorsonly: out(' Total number of warnings: {count:5}'.format( count=total_warning_count)) out(' Total number of errors: {count:5}'.format( count=total_error_count)) out('') file_counts = [ (counts[0], counts[1], fn.split(os.sep)[-3], fn.split(os.sep)[-1]) for (fn, counts) in files_to_errors.items() ] # If we're showing errors only, then don't talk about warnings. if errorsonly: header = 'Errors Filename' line = ' {errors:5} {locale} ({fn})' else: header = 'Warnings Errors Filename' line = ' {warnings:5} {errors:5} {locale} ({fn})' file_counts = list(reversed(sorted(file_counts))) printed_header = False for error_count, warning_count, locale, fn in file_counts: if not error_count and not warning_count: continue if not printed_header: out(header) printed_header = True out(line.format( warnings=warning_count, errors=error_count, fn=fn, locale=locale)) # Return 0 if everything was fine or 1 if there were errors. ctx.exit(code=1 if total_error_count else 0)
def lint(ctx, quiet, color, varformat, rules, excluderules, reporter, errorsonly, path): """ Lints .po/.pot files for issues You can ignore rules on a string-by-string basis by adding an extracted comment "dennis-ignore: <comma-separated-rules>". See documentation for details. """ global TERM if not quiet: out('dennis version {version}'.format(version=__version__)) if not color: TERM = FauxTerminal() # Make sure requested rules are valid all_rules = get_linter_rules(with_names=True) all_rules.update(get_template_linter_rules(with_names=True)) rules = [rule.strip() for rule in rules.split(',') if rule.strip()] invalid_rules = [rule for rule in rules if rule not in all_rules] if invalid_rules: raise click.UsageError('invalid rules: %s.' % ', '.join(invalid_rules)) if not rules: rules = get_linter_rules() rules.update(get_template_linter_rules()) rules = rules.keys() if excluderules: excludes = [rule.strip() for rule in excluderules.split(',') if rule.strip()] invalid_rules = [rule for rule in excludes if rule not in all_rules] if invalid_rules: raise click.UsageError('invalid exclude rules: %s.' % ', '.join(invalid_rules)) # Remove excluded rules rules = [rule for rule in rules if rule not in excludes] # Build linters and lint linter = Linter(varformat.split(','), rules) templatelinter = TemplateLinter(varformat.split(','), rules) po_files = [] for item in path: if os.path.isdir(item): for root, dirs, files in os.walk(item): po_files.extend( [os.path.join(root, fn) for fn in files if fn.endswith(('.po', '.pot'))]) else: po_files.append(item) po_files = [os.path.abspath(fn) for fn in po_files if fn.endswith(('.po', '.pot'))] if not po_files: raise click.UsageError('nothing to work on. Use --help for help.') files_to_errors = {} total_error_count = 0 total_warning_count = 0 total_files_with_errors = 0 for fn in po_files: try: if not os.path.exists(fn): raise click.UsageError('File "{fn}" does not exist.'.format( fn=click.format_filename(fn))) if fn.endswith('.po'): results = linter.verify_file(fn) else: results = templatelinter.verify_file(fn) except IOError as ioe: # This is not a valid .po file. So mark it as an error. err('>>> Problem opening file: {fn}'.format( fn=click.format_filename(fn))) err(repr(ioe)) out('') # FIXME - should we track this separately as an invalid # file? files_to_errors[fn] = (1, 0) total_error_count += 1 continue if errorsonly: # Go through and nix all the non-error LintMessages results = [res for res in results if res.kind == 'err'] # We don't want to print output for files that are fine, so we # update the bookkeeping and move on. if not results: files_to_errors[fn] = (0, 0) continue if not quiet and not reporter: out(TERM.bold_green, '>>> Working on: {fn}'.format(fn=click.format_filename(fn)), TERM.normal) error_results = [res for res in results if res.kind == 'err'] warning_results = [res for res in results if res.kind == 'warn'] error_count = len(error_results) total_error_count += error_count warning_count = len(warning_results) total_warning_count += warning_count if not quiet: for msg in error_results: if reporter == 'line': out(fn, ':', str(msg.poentry.linenum), ':', '0', ':', msg.code, ':', msg.msg) else: out(TERM.bold_red, msg.code, ': ', msg.msg, TERM.normal) out(withlines(msg.poentry.linenum, msg.poentry.original)) out('') if not quiet and not errorsonly: for msg in warning_results: if reporter == 'line': out(fn, ':', str(msg.poentry.linenum), ':', '0', ':', msg.code, ':', msg.msg) else: out(TERM.bold_yellow, msg.code, ': ', msg.msg, TERM.normal) out(withlines(msg.poentry.linenum, msg.poentry.original)) out('') files_to_errors[fn] = (error_count, warning_count) if error_count > 0: total_files_with_errors += 1 if not quiet and reporter != 'line': out('Totals') if not errorsonly: out(' Warnings: {warnings:5}'.format(warnings=warning_count)) out(' Errors: {errors:5}\n'.format(errors=error_count)) if len(po_files) > 1 and not quiet and reporter != 'line': out('Final totals') out(' Number of files examined: {count:5}'.format( count=len(po_files))) out(' Total number of files with errors: {count:5}'.format( count=total_files_with_errors)) if not errorsonly: out(' Total number of warnings: {count:5}'.format( count=total_warning_count)) out(' Total number of errors: {count:5}'.format( count=total_error_count)) out('') file_counts = [ (counts[0], counts[1], fn.split(os.sep)[-3], fn.split(os.sep)[-1]) for (fn, counts) in files_to_errors.items() ] # If we're showing errors only, then don't talk about warnings. if errorsonly: header = 'Errors Filename' line = ' {errors:5} {locale} ({fn})' else: header = 'Warnings Errors Filename' line = ' {warnings:5} {errors:5} {locale} ({fn})' file_counts = list(reversed(sorted(file_counts))) printed_header = False for error_count, warning_count, locale, fn in file_counts: if not error_count and not warning_count: continue if not printed_header: out(header) printed_header = True out(line.format( warnings=warning_count, errors=error_count, fn=fn, locale=locale)) # Return 0 if everything was fine or 1 if there were errors. ctx.exit(code=1 if total_error_count else 0)
def lint_cmd(scriptname, command, argv): """Lints a .po file or directory of files.""" if not '--quiet' in argv and not '-q' in argv: print 'dennis version {version}'.format(version=__version__) parser = build_parser( 'usage: %prog lint [ DIR | FILENAME <FILENAME> ... ]', description='Lints a .po file for mismatched Python string ' 'formatting tokens.', sections=[ (format_vars(), True), (format_lint_rules(), True), ]) parser.add_option( '--vars', dest='vars', help=('Comma-separated list of variable types. See Available Variable ' 'Formats.'), metavar='VARS', default='python') parser.add_option( '--rules', dest='rules', help=('Comma-separated list of lint rules to use. Defaults to all ' 'rules. See Available Lint Rules.'), metavar='RULES', default='') parser.add_option( '-q', '--quiet', action='store_true', dest='quiet', help='quiet all output') parser.add_option( '--errorsonly', action='store_true', dest='errorsonly', help='only print errors') (options, args) = parser.parse_args(argv) if not args: parser.print_help() return 1 linter = Linter(options.vars.split(','), options.rules.split(',')) if os.path.isdir(args[0]): po_files = [] for root, dirs, files in os.walk(args[0]): po_files.extend( [os.path.join(root, fn) for fn in files if fn.endswith('.po')]) else: po_files = args po_files = [os.path.abspath(fn) for fn in po_files if fn.endswith('.po')] files_to_errors = {} total_error_count = 0 total_warning_count = 0 total_files_with_errors = 0 for fn in po_files: try: if not os.path.exists(fn): raise IOError('File "{fn}" does not exist.'.format(fn=fn)) results = linter.verify_file(fn) except IOError as ioe: # This is not a valid .po file. So mark it as an error. print TERMINAL.bold_red('>>> Error opening file: {fn}'.format( fn=fn)) print TERMINAL.bold_red(ioe.message) print '' # FIXME - should we track this separately as an invalid # file? files_to_errors[fn] = (1, 0) total_error_count += 1 continue # Extract all the problematic LintItems--they have non-empty # missing or invalid lists. problem_results = [ r for r in results if r.has_problems(options.errorsonly)] # We don't want to print output for files that are fine, so we # update the bookkeeping and move on. if not problem_results: files_to_errors[fn] = (0, 0) continue if not options.quiet: print TERMINAL.bold_green('>>> Working on: {fn}'.format(fn=fn)) output = [] error_count = 0 warning_count = 0 for entry in problem_results: total_error_count += len(entry.errors) error_count += len(entry.errors) if not options.quiet: # TODO: This is totally s***e code. for code, trstr, msg in entry.errors: output.append(TERMINAL.bold_red(u'Error: {0}: {1}'.format( code, msg))) for field, s in zip(trstr.msgid_fields, trstr.msgid_strings): output.append(u'{0} "{1}"'.format(field, s)) output.append(u'{0} "{1}"\n'.format( trstr.msgstr_field, trstr.msgstr_string)) total_warning_count += len(entry.warnings) warning_count += len(entry.warnings) if not options.quiet and not options.errorsonly: for code, trstr, msg in entry.warnings: output.append(TERMINAL.bold_yellow(u'Warning: {0}: {1}'.format( code, msg))) for field, s in zip(trstr.msgid_fields, trstr.msgid_strings): output.append(u'{0} "{1}"'.format(field, s)) output.append(u'{0} "{1}"\n'.format( trstr.msgstr_field, trstr.msgstr_string)) files_to_errors[fn] = (error_count, warning_count) if error_count > 0: total_files_with_errors += 1 if not options.quiet: output.append(u'Totals') if not options.errorsonly: output.append(u' Warnings: {warnings:5}'.format(warnings=warning_count)) output.append(u' Errors: {errors:5}\n'.format(errors=error_count)) print_utf8(u'\n'.join(output)) if len(po_files) > 1 and not options.quiet: print 'Final totals' print ' Number of files examined: {count:5}'.format( count=len(po_files)) print ' Total number of files with errors: {count:5}'.format( count=total_files_with_errors) if not options.errorsonly: print ' Total number of warnings: {count:5}'.format( count=total_warning_count) print ' Total number of errors: {count:5}'.format( count=total_error_count) print '' file_counts = [ (counts[0], counts[1], fn.split(os.sep)[-3], fn.split(os.sep)[-1]) for (fn, counts) in files_to_errors.items() ] # If we're showing errors only, then don't talk about warnings. if options.errorsonly: header = 'Errors Filename' line = ' {errors:5} {locale} ({fn})' else: header = 'Warnings Errors Filename' line = ' {warnings:5} {errors:5} {locale} ({fn})' print header file_counts = reversed(sorted(file_counts)) for error_count, warning_count, locale, fn in file_counts: if not error_count and not warning_count: continue print line.format( warnings=warning_count, errors=error_count, fn=fn, locale=locale) # Return 0 if everything was fine or 1 if there were errors. return 1 if total_error_count else 0
def lint_cmd(scriptname, command, argv): """Lints a .po file or directory of files.""" if not '--quiet' in argv: print '%s version %s' % (scriptname, __version__) parser = build_parser( 'usage: %prog lint [ FILE | DIR ]', description='Lints a .po file for mismatched Python string ' 'formatting tokens.', sections=[ (format_types(), True), ]) parser.add_option( '-t', '--types', dest='types', help='Comma-separated list of variable types. See Available Types.', metavar='TYPES', default='python') parser.add_option( '-q', '--quiet', action='store_true', dest='quiet', help='quiet all output') parser.add_option( '--errorsonly', action='store_true', dest='errorsonly', help='only prints errors') (options, args) = parser.parse_args(argv) if not args: parser.print_help() return 1 linter = Linter(options.types.split(',')) if os.path.isdir(args[0]): po_files = [] for root, dirs, files in os.walk(args[0]): po_files.extend( [os.path.join(root, fn) for fn in files if fn.endswith('.po')]) else: po_files = [args[0]] files_to_errors = {} total_error_count = 0 total_warning_count = 0 total_files_with_errors = 0 for fn in po_files: if not fn.endswith('.po'): continue fn = os.path.abspath(fn) try: if not os.path.exists(fn): raise IOError('File "{fn}" does not exist.'.format(fn=fn)) results = linter.verify_file(fn) except IOError as ioe: # This is not a valid .po file. So mark it as an error. print TERMINAL.bold_red('>>> Error opening file: {fn}'.format( fn=fn)) print TERMINAL.bold_red(ioe.message) print '' # FIXME - should we track this separately as an invalid # file? files_to_errors[fn] = (1, 0) total_error_count += 1 continue # Extract all the problematic LintItems--they have non-empty # missing or invalid lists. problem_results = [r for r in results if r.missing or r.invalid] # We don't want to print output for files that are fine, so we # update the bookkeeping and move on. if not problem_results: files_to_errors[fn] = (0, 0) continue if not options.quiet: print TERMINAL.bold_green('>>> Working on: {fn}'.format(fn=fn)) error_count = 0 warning_count = 0 for result in problem_results: if not result: continue if result.invalid: total_error_count += 1 error_count += 1 if result.missing: total_warning_count += 1 warning_count += 1 if not options.quiet and (result.invalid or not options.errorsonly): print_lint_error(linter.vartok, result, options.errorsonly) files_to_errors[fn] = (error_count, warning_count) if error_count > 0: total_files_with_errors += 1 if not options.quiet: print 'Totals' print ' Warnings: {warnings:5}'.format(warnings=warning_count) print ' Errors: {errors:5}'.format(errors=error_count) print '' if len(po_files) > 1 and not options.quiet: print 'Final Tally:' print '' print 'Number of files examined: {count:5}'.format( count=len(po_files)) print 'Total number of files with errors: {count:5}'.format( count=total_files_with_errors) print 'Total number of warnings: {count:5}'.format( count=total_warning_count) print 'Total number of errors: {count:5}'.format( count=total_error_count) print '' file_counts = [ (counts[0], counts[1], fn.split(os.sep)[-3], fn.split(os.sep)[-1]) for (fn, counts) in files_to_errors.items()] print 'Warnings Errors Filename' file_counts = reversed(sorted(file_counts)) for error_count, warning_count, locale, fn in file_counts: if not error_count and not warning_count: continue print ' {warnings:5} {errors:5} {locale} ({fn})'.format( warnings=warning_count, errors=error_count, fn=fn, locale=locale) # Return 0 if everything was fine or 1 if there were errors. return 1 if total_error_count else 0