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

import sys, string, tokenize, pprint
##import os, getopt
import __builtin__

warnings = []

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

def log_warn(file, linenum, line, message, start=0, end=0):
    warnings.append( (file, linenum, message) )

warn = log_warn
opt_i = 0
ignore_subobjs = 1

##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 true false') + dir(__builtin__): special[name] = 1
member = {}
for name in [].__methods__ + {}.__methods__ + [].append.__members__ + \
    warn.__members__ + warn.func_code.__members__ + \
    tb.__members__ + tb.tb_frame.__members__ + \
    ['__dict__', '__methods__', '__members__']: member[name] = 1
#    file.__methods__ + file.__members__ + \
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 = {}, {}, {}

def init_globals(afilename):
    global warnings, position, modules, defined, modfrom, sawimport, last, parent, filename, bracket_depth

    warnings[:] = []
    position.clear(); modules.clear(); defined.clear()
    modfrom = sawimport = 0
    last = parent = ''
    filename = afilename
    bracket_depth = 0

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

    if token == 'import': sawimport = 1
    elif token in ('\r\n', '\n', ';'): modfrom, sawimport = '', 0
    elif token == '.': parent = last
    elif token == '(': bracket_depth = bracket_depth + 1
    elif token == ')': bracket_depth = bracket_depth - 1
    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
            # RB:
            # Added the following two cases:
            #  Optionally ignore all sub_obj if not in importing mode
            #    Boa can't use importing mode for fear of interpreter pollution
            #  Ignore all wx* names these are from wxPython and usually imported
            #    with a from wxPython.* import *
            #    Maybe test should rather be directly against the wxPython
            #    namespaces
            # --
            elif parent and parent == 'self':
                special[token] = 1
            elif parent and ignore_subobjs and not opt_i: pass
            elif len(token) >= 3 and token[:2] == 'wx' and token[2] in string.uppercase: pass
            elif len(token) >= 5 and token[:4] == 'EVT_': 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(filename, srow, line,
                        'dubious reserved name "%s"' % token, scol, ecol)
            elif last != 'from': position[token] = (srow, line, scol, ecol, bracket_depth)

        finally:
            last, parent = token, ''

def pylint(afile, filename):
##    print 'RESERVED'
##    pprint.pprint(reserved)
##    print 'SPECIAL'
##    pprint.pprint(special)
    init_globals(filename)
    try: tokenize.tokenize(afile.readline, count)
    except tokenize.TokenError, message:
        warn(filename, 0, '', message)
        sys.exit(1)
    
    unique = []
    for name, (linenum, line, start, end, brk_dpth) in position.items():
        unique.append( (linenum, (start, end, name, line, brk_dpth)) )
    unique.sort()
    for linenum, (start, end, name, line, brk_dpth) in unique:
        warn(filename, linenum, line, ('"%s" used only once (%d)',
             '"%s" defined but unused (%d)')[defined.has_key(name)] % (name, brk_dpth), start, end)

if __name__ == '__main__':
    pylint(open('pylint.py'), 'pylint.py')
    pprint.pprint(warnings)







