#!/usr/bin/python -tt
# encoding=UTF-8

# Copyright © 2011, 2012, 2013 Jakub Wilk
#
# This program is free software.  It is distributed under the terms of the GNU
# General Public License as published by the Free Software Foundation; either
# version 2 of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 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, you can find it on the World Wide Web at
# http://www.gnu.org/copyleft/gpl.html, or write to the Free Software
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.

import ast
import itertools
import os
import sys

builtin_exception_types = set()

def load_data():
    lintian_root = os.environ['LINTIAN_ROOT']
    with open('{root}/vendors/debian/python/data/python-exceptions'.format(root=lintian_root)) as file:
        for line in file:
            line = line.strip()
            if not line:
                continue
            if line.startswith('#'):
                continue
            builtin_exception_types.add(line)

if sys.version_info >= (3, 2):
    import tokenize
    python_open = tokenize.open
    del tokenize
elif sys.version_info >= (3,):
    import py_compile
    def python_open(filename, read_encoding=py_compile.read_encoding):
        encoding = read_encoding(filename, 'utf-8')
        return open(filename, 'rU', encoding=encoding)
    del py_compile
else:
    def python_open(filename):
        return open(filename, 'rU')

def tag(*s):
    return s

def check_node(node):
    if isinstance(node, ast.Raise):
        try:
            ex_type = node.type
        except AttributeError:
            ex_type = node.exc
        while isinstance(ex_type, ast.BinOp):
            ex_type = ex_type.left
        if isinstance(ex_type, ast.Str):
            yield tag('string-exception', node.lineno)
    elif isinstance(node, ast.ExceptHandler):
        node_name = None
        if isinstance(node.name, ast.Name):
            # Python 2
            node_name = node.name.id
        elif isinstance(node.name, str):
            # Python 3
            node_name = node.name
        if node_name in builtin_exception_types:
            yield tag('except-shadows-builtin', node.lineno, node_name)
        del node_name
    elif isinstance(node, ast.Assert):
        if isinstance(node.test, ast.Tuple) and len(node.test.elts) > 0:
            yield tag('assertion-always-true', node.lineno)
    for child in ast.iter_child_nodes(node):
        for t in check_node(child):
            yield t

def check_file(filename):
    with python_open(filename) as file:
        contents = file.read()
    return check_contents(contents)

def check_contents(contents, catch_tab_errors=True):
    if sys.version_info >= (3,):
        # Inconsistent use of tabs and spaces in indentation is always a fatal
        # error in Python 3.X.
        catch_tab_errors = False
    try:
        contents = ast.parse(contents)
    except TabError as exc:
        if catch_tab_errors:
            contents = contents.expandtabs()
            return itertools.chain(
                [tag('inconsistent-use-of-tabs-and-spaces-in-indentation', exc.lineno)],
                check_contents(contents, catch_tab_errors=False)
            )
        else:
            return [tag('syntax-error', exc.lineno, exc.msg)]
    except SyntaxError as exc:
        return [tag('syntax-error', exc.lineno, exc.msg)]
    except Exception as exc:
        return [tag('syntax-error', '-', str(exc))]
    return check_node(contents)

def main(self):
    load_data()
    for filename in sys.argv[1:]:
        print('# {0}'.format(filename.replace('\n', '?')))
        for t in check_file(filename):
            print(' '.join(map(str, t)))

if __name__ == '__main__':
    main(sys.argv[1:])

# Local Variables:
# indent-tabs-mode: nil
# End:
# vim: syntax=python sw=4 sts=4 sr et
