#!/bin/env python
# Simple-minded Python identifier checker (Ka-Ping Yee, 1 April 1997)

import sys, getopt, string, tokenize, os

def terse_warn(file, linenum, line, message, start=0, end=0):
    print '%s:%d: %s' % (file, linenum, message)

def tty_warn(file, linenum, line, message, start=0, end=0):
    print '%s:%d: %s\n  %s' % (file, linenum, message, line),

BOLD, NORMAL = '\x1b[1m', '\x1b[0m'
def vt100_warn(file, linenum, line, message, start=0, end=0):
    print '%s:%d: %s' % (file, linenum, message)
    print '  ' + line[:start] + BOLD + line[start:end] + NORMAL + line[end:],

vt100_compatible = ('vt100', 'vt102', 'xterm', 'ansi', 'iris-ansi', 'linux')

options, args = getopt.getopt(sys.argv[1:], 'vhi')
if not args or ('-h', '') in options:
    print "PyLint (1 April 1997) by Ka-Ping Yee"
    print "usage: %s [-v] [-i] filename.py" % sys.argv[0]
    print "    -v for verbose mode (display source lines)"
    print "    -i to import modules that are imported in the file"
    sys.exit(0)

if args[0] == '-': file = sys.stdin
else: file = open(args[0])
opt_i = ('-i', '') in options
if ('-v', '') not in options:
    warn = terse_warn
elif os.environ.has_key('TERM') and os.environ['TERM'] in vt100_compatible:
    warn = vt100_warn
else:
    warn = tty_warn

try: 1/0
except: tb = sys.exc_traceback
special = {}
for name in string.split('and break class continue def del elif else except '
    'exec finally for from global if import in is lambda not or pass print '
    'raise return try while') + dir(__builtins__): special[name] = 1
member = {}
for name in [].__methods__ + {}.__methods__ + [].append.__members__ + \
    warn.__members__ + warn.func_code.__members__ + \
    file.__methods__ + file.__members__ + \
    tb.__members__ + tb.tb_frame.__members__ + \
    ['__dict__', '__methods__', '__members__']: member[name] = 1
reserved = {}
for name in string.split('abs add and bases builtin builtins call class cmp '
    'coerce copy copyright deepcopy del delattr delitem delslice div divmod '
    'file float getattr getinitargs getitem getslice getstate hash hello hex '
    'init int invert len long lshift main mod mul name neg nonzero oct or pos '
    'pow radd rand rcmp rdiv rdivmod repr rlshift rmod rmul ror rpow rrshift '
    'rshift rsub rxor setattr setitem setslice setstate str sub version xor'):
    reserved['__'+name+'__'] = 1

position, modules, defined = {}, {}, {}
modfrom = sawimport = 0
last = parent = ''

def count(type, token, (srow, scol), (erow, ecol), line,
    seen = special.has_key, imported = modules.has_key):
    global sawimport, modfrom, last, parent, modules

    if token == 'import': sawimport = 1
    elif token in ('\r\n', '\n', ';'): modfrom, sawimport = '', 0
    elif token == '.': parent = last
    else:
        try:
            if last == 'from': modfrom = token
            elif last in ('class', 'def'): defined[token] = 1
            elif opt_i and modfrom and token == '*':
                try:
                    for name in dir(__import__(modfrom)):
                        if name[0] != '_': special[name] = 1
                except: pass
            if type != tokenize.NAME or seen(token): return
            if opt_i and not modfrom and sawimport and not imported(token):
                try:
                    modules[token] = {}
                    for name in dir(__import__(token)):
                        modules[token][name] = 1
                except: pass
            if parent and member.has_key(token): pass
            elif imported(parent) and modules[parent].has_key(token): pass
            elif position.has_key(token):
                del position[token]
                special[token] = 1
            elif token[:2] == '__' == token[-2:]:
                if not reserved.has_key(token):
                    warn(file.name, srow, line,
                        'dubious reserved name "%s"' % token, scol, ecol)
            elif last != 'from': position[token] = (srow, line, scol, ecol)

        finally:
            last, parent = token, ''

try: tokenize.tokenize(file.readline, count)
except tokenize.TokenError, message:
    warn(file.name, 0, '', message)
    sys.exit(1)

unique = []
for name, (linenum, line, start, end) in position.items():
    unique.append(linenum, (start, end, name, line))
unique.sort()
for linenum, (start, end, name, line) in unique:
    warn(file.name, linenum, line, ('"%s" used only once',
         '"%s" defined but unused')[defined.has_key(name)] % name, start, end)
