tools/lint/flake8.lint
author Xidorn Quan <me@upsuper.org>
Wed, 13 Jul 2016 10:28:28 +1000
changeset 304807 a43543bd1f595cfd22d768c72dbd16081a34090c
parent 303481 ce5c5ebc122bf9d776b30dfa359dfa79ee50ec21
child 306106 3ec6380fca8207fa199693206a3ca24975bf9833
permissions -rw-r--r--
Bug 1285069 part 2 - Remove nsPointerLockPermissionRequest, only keep the Allow method under a different name. r=smaug We still want to keep ApplyPointerLock asynchronous so that the timing when pointer lock takes effect is not changed by this patch. MozReview-Commit-ID: EA8c6uzOd8F

# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
# vim: set filetype=python:
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.

import json
import os
import signal

import which
from mozprocess import ProcessHandler

from mozlint import result


FLAKE8_NOT_FOUND = """
Could not find flake8! Install flake8 and try again.

    $ pip install flake8
""".strip()


LINE_OFFSETS = {
    # continuation line under-indented for hanging indent
    'E121': (-1, 2),
    # continuation line missing indentation or outdented
    'E122': (-1, 2),
    # continuation line over-indented for hanging indent
    'E126': (-1, 2),
    # continuation line over-indented for visual indent
    'E127': (-1, 2),
    # continuation line under-indented for visual indent
    'E128': (-1, 2),
    # continuation line unaligned for hanging indend
    'E131': (-1, 2),
    # expected 1 blank line, found 0
    'E301': (-1, 2),
    # expected 2 blank lines, found 1
    'E302': (-2, 3),
}
"""Maps a flake8 error to a lineoffset tuple.

The offset is of the form (lineno_offset, num_lines) and is passed
to the lineoffset property of `ResultContainer`.
"""

results = []


def process_line(line):
    try:
        res = json.loads(line)
    except ValueError:
        return

    if 'code' in res:
        if res['code'].startswith('W'):
            res['level'] = 'warning'

        if res['code'] in LINE_OFFSETS:
            res['lineoffset'] = LINE_OFFSETS[res['code']]

    results.append(result.from_linter(LINTER, **res))


def run_process(cmdargs):
    # flake8 seems to handle SIGINT poorly. Handle it here instead
    # so we can kill the process without a cryptic traceback.
    orig = signal.signal(signal.SIGINT, signal.SIG_IGN)
    proc = ProcessHandler(cmdargs, env=os.environ,
                          processOutputLine=process_line)
    proc.run()
    signal.signal(signal.SIGINT, orig)

    try:
        proc.wait()
    except KeyboardInterrupt:
        proc.kill()


def lint(files, **lintargs):
    binary = os.environ.get('FLAKE8')
    if not binary:
        try:
            binary = which.which('flake8')
        except which.WhichError:
            pass

    if not binary:
        print(FLAKE8_NOT_FOUND)
        return []

    cmdargs = [
        binary,
        '--format', '{"path":"%(path)s","lineno":%(row)s,'
                    '"column":%(col)s,"rule":"%(code)s","message":"%(text)s"}',
    ]

    exclude = lintargs.get('exclude')
    if exclude:
        cmdargs += ['--exclude', ','.join(lintargs['exclude'])]

    # Run any paths with a .flake8 file in the directory separately so
    # it gets picked up. This means only .flake8 files that live in
    # directories that are explicitly included will be considered.
    # See bug 1277851
    no_config = []
    for f in files:
        if not os.path.isfile(os.path.join(f, '.flake8')):
            no_config.append(f)
            continue
        run_process(cmdargs+[f])

    if no_config:
        run_process(cmdargs+no_config)

    return results


LINTER = {
    'name': "flake8",
    'description': "Python linter",
    'include': [
        'python/mozlint',
        'tools/lint',
        'taskcluster',
        'testing/marionette/client',
        'testing/talos/',
    ],
    'exclude': [],
    'type': 'external',
    'payload': lint,
}