Пример #1
0
    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
Пример #2
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
Пример #3
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
Пример #4
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}'])
Пример #5
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
Пример #6
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)
Пример #7
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)
Пример #8
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
Пример #9
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
Пример #10
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)
Пример #11
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
Пример #12
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)
Пример #13
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, [])
Пример #14
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)
Пример #15
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)
Пример #16
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)
Пример #17
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
Пример #18
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