#!/usr/bin/python

# u1lint: Wrapper script for pylint or pyflakes
#
# Author: Rodney Dawes <rodney.dawes@canonical.com>
#
# Copyright 2009-2010 Canonical Ltd.
#
# This program is free software: you can redistribute it and/or modify it 
# under the terms of the GNU General Public License version 3, as published 
# by the Free Software Foundation.
#
# This program is distributed in the hope that it will be useful, but 
# WITHOUT ANY WARRANTY; without even the implied warranties of 
# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR 
# PURPOSE.  See the GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License along 
# with this program.  If not, see <http://www.gnu.org/licenses/>.
"""Wrapper script for pylint command"""

import ConfigParser
import os
import subprocess

from xdg.BaseDirectory import xdg_data_dirs

SRCDIR = os.environ.get('SRCDIR', os.getcwd())

def find_pylintrc():
    """Return the first pylintrc found."""
    # Use the pylintrc in the source tree if there is one
    full_name = os.path.join(SRCDIR, 'pylintrc')
    if os.path.exists(full_name):
        return full_name

    # If no pylintrc in the source tree, use the first in $XDG_DATA_DIRS
    # Hopefully this is the one we installed, and hasn't been overridden
    for name in xdg_data_dirs:
        full_name = os.path.join(name, 'ubuntuone-dev-tools', 'pylintrc')
        if os.path.exists(full_name):
            return full_name
    return None

PYLINTRC = find_pylintrc()

def _read_pylintrc_ignored():
    """Get the ignored files list from pylintrc"""
    try:
        config = ConfigParser.ConfigParser()
        config.read([PYLINTRC])

        return config.get("MASTER", "ignore").split(",")
    except (TypeError, ConfigParser.NoOptionError):
        return None

def _group_lines_by_file(data):
    """Format file:line:message output as lines grouped by file."""
    did_fail = False
    outputs = []
    filename = ""
    for line in data.splitlines():
        current = line.split(":", 3)
        if line.startswith("    "):
            outputs.append("    " + current[0] + "")
        elif line.startswith("build/") or len(current) < 3:
            pass
        elif filename == current[0]:
            # pylint warning W0511 is a custom note
            if not "[W0511]" in current[2]:
                did_fail = True
            outputs.append("    " + current[1] + ": " + current[2])
        elif filename != current[0]:
            filename = current[0]
            outputs.append("")
            outputs.append(filename + ":")
            # pylint warning W0511 is a custom note
            if not "[W0511]" in current[2]:
                did_fail = True
            outputs.append("    " + current[1] + ": " + current[2])

    return (did_fail, "\n".join(outputs))

def _find_files():
    """Find all Python files under the current tree."""
    pyfiles = []
    # pylint: disable=W0612
    for root, dirs, files in os.walk(SRCDIR, topdown=False):
        for filename in files:
            filepath = "%s/" % root

            # Skip files in build/
            if filepath.startswith("%s/build/" % SRCDIR):
                continue

            # Skip protobuf-generated and backup files
            if filename.endswith("_pb2.py") or filename.endswith("~"):
                continue

            if filename.endswith(".py") or filepath.endswith("bin/"):
                pyfiles.append(os.path.join(root, filename))

    pyfiles.sort()
    return pyfiles


failed = False

ignored = _read_pylintrc_ignored()

# So that we can match the path correctly
if ignored:
    moreignores = [os.path.join(SRCDIR, item) for item in ignored]
    ignored.extend(moreignores)
else:
    ignored = []

if os.environ.get('USE_PYFLAKES'):
    pylint_args = ["pyflakes"]
else:
    pylint_args = ["pylint",
                   "--output-format=parseable",
                   "--include-ids=yes",]
    if PYLINTRC:
        pylint_args.append("--rcfile=" + PYLINTRC)
    
for path in _find_files():
    if path not in ignored and not path.startswith(os.path.join(SRCDIR,
                                                                "_build")):
        pylint_args.append(path)

p = subprocess.Popen(pylint_args,
                     bufsize=4096, stdout=subprocess.PIPE)
notices = p.stdout

output = "".join(notices.readlines())
if output != "":
    print "== Python Lint Notices =="
    (failed, grouped) = _group_lines_by_file(output)
    print grouped
    print ""

returncode = p.wait()
if returncode != 0:
    exit(returncode)

if failed:
    exit(1)
