# -*- Mode: Python -*-
# vi:si:et:sw=4:sts=4:ts=4

# Copyright (c) 2001-2004, MetaSlash Inc.  All rights reserved.
# Portions Copyright (c) 2005, Google, Inc. All rights reserved.

"""
Setup a lot of info about Python builtin types, functions, methods, etc.
"""

import types
import sys

from pychecker import utils
from pychecker import Stack
from pychecker import Warning


BOOL = types.IntType
#                    name   (type,  args: min, max, kwArgs?
GLOBAL_FUNC_INFO = { '__import__': (types.ModuleType, 1, 4),
                     'abs': (Stack.TYPE_UNKNOWN, 1, 1),
                     'apply': (Stack.TYPE_UNKNOWN, 2, 3),
                     'buffer': (types.BufferType, 1, 3),
                     'callable': (BOOL, 1, 1),
                     'chr': (types.StringType, 1, 1),
                     'cmp': (types.IntType, 2, 2),
                     'coerce': ([ types.NoneType, types.TupleType ], 2, 2),
                     'compile': (types.CodeType, 3, 3),
                     'complex': (types.ComplexType, 1, 2, ['real', 'imag']),
                     'delattr': (types.NoneType, 2, 2),
                     'dir': (types.ListType, 0, 1),
                     'divmod': (types.TupleType, 2, 2),
                     'eval': (Stack.TYPE_UNKNOWN, 1, 3),
                     'execfile': (types.NoneType, 1, 3),
                     'filter': (types.ListType, 2, 2),
                     'float': (types.FloatType, 1, 1),
                     'getattr': (Stack.TYPE_UNKNOWN, 2, 3),
                     'globals': (types.DictType, 0, 0),
                     'hasattr': (BOOL, 2, 2),
                     'hash': (types.IntType, 1, 1),
                     'hex': (types.StringType, 1, 1),
                     'id': (types.IntType, 1, 1),
                     'input': (Stack.TYPE_UNKNOWN, 0, 1),
                     'int': (types.IntType, 1, 2, ['x']),
                     'intern': (types.StringType, 1, 1),
                     'isinstance': (BOOL, 2, 2),
                     'issubclass': (BOOL, 2, 2),
                     'len': (types.IntType, 1, 1),
                     'list': (types.ListType, 1, 1, ['sequence']),
                     'locals': (types.DictType, 0, 0),
                     'long': (types.LongType, 1, 2, ['x']),
                     'map': (types.ListType, 2, None),
                     'max': (Stack.TYPE_UNKNOWN, 1, None),
                     'min': (Stack.TYPE_UNKNOWN, 1, None),
                     'oct': (types.StringType, 1, 1),
                     'open': (types.FileType, 1, 3, ['name', 'mode', 'buffering']),
                     'ord': (types.IntType, 1, 1),
                     'pow': (Stack.TYPE_UNKNOWN, 2, 3),
                     'range': (types.ListType, 1, 3),
                     'raw_input': (types.StringType, 0, 1),
                     'reduce': (Stack.TYPE_UNKNOWN, 2, 3),
                     'reload': (types.ModuleType, 1, 1),
                     'repr': (types.StringType, 1, 1),
                     'round': (types.FloatType, 1, 2),
                     'setattr': (types.NoneType, 3, 3),
                     'slice': (types.SliceType, 1, 3),
                     'str': (types.StringType, 1, 1),
                     'tuple': (types.TupleType, 1, 1),
                     'type': (types.TypeType, 1, 1),
                     'vars': (types.DictType, 0, 1),
                     'xrange': (types.ListType, 1, 3),
                   }

if hasattr(types, 'UnicodeType') :
    GLOBAL_FUNC_INFO['unichr'] = (types.UnicodeType, 1, 1)
    GLOBAL_FUNC_INFO['unicode'] = (types.UnicodeType, 1, 3, ['string', 'encoding', 'errors'])

if utils.pythonVersion() >= utils.PYTHON_2_2 :
    GLOBAL_FUNC_INFO['compile'] = (types.CodeType, 3, 5)
    GLOBAL_FUNC_INFO['dict'] = (types.DictType, 0, 1, ['items'])
    GLOBAL_FUNC_INFO['file'] = GLOBAL_FUNC_INFO['open']
    GLOBAL_FUNC_INFO['float'] = (types.FloatType, 0, 1, ['x'])
    GLOBAL_FUNC_INFO['int'] = (types.IntType, 0, 2, ['x'])
    GLOBAL_FUNC_INFO['list'] = (types.ListType, 0, 1, ['sequence'])
    GLOBAL_FUNC_INFO['long'] = (types.LongType, 0, 2, ['x'])
    GLOBAL_FUNC_INFO['str'] = (types.StringType, 0, 1, ['object'])
    # FIXME: type doesn't take 2 args, only 1 or 3
    GLOBAL_FUNC_INFO['type'] = (types.TypeType, 1, 3, ['name', 'bases', 'dict'])
    GLOBAL_FUNC_INFO['tuple'] = (types.TupleType, 0, 1, ['sequence'])

    GLOBAL_FUNC_INFO['classmethod'] = (types.MethodType, 1, 1)
    GLOBAL_FUNC_INFO['iter'] = (Stack.TYPE_UNKNOWN, 1, 2)
    GLOBAL_FUNC_INFO['property'] = (Stack.TYPE_UNKNOWN, 0, 4, ['fget', 'fset', 'fdel', 'doc'])
    GLOBAL_FUNC_INFO['super'] = (Stack.TYPE_UNKNOWN, 1, 2)
    GLOBAL_FUNC_INFO['staticmethod'] = (types.MethodType, 1, 1)
    GLOBAL_FUNC_INFO['unicode'] = (types.UnicodeType, 0, 3, ['string', 'encoding', 'errors'])

    GLOBAL_FUNC_INFO['bool'] = (BOOL, 1, 1, ['x'])

if utils.pythonVersion() >= utils.PYTHON_2_3:
    GLOBAL_FUNC_INFO['dict'] = (types.DictType, 0, 1, [])

if utils.pythonVersion() >= utils.PYTHON_2_5:
    GLOBAL_FUNC_INFO['max'] = (Stack.TYPE_UNKNOWN, 1, None, ['key'])
    GLOBAL_FUNC_INFO['min'] = (Stack.TYPE_UNKNOWN, 1, None, ['key'])

def tryAddGlobal(name, *args):
    if globals().has_key(name):
        GLOBAL_FUNC_INFO[name] = args

zipMinArgs = 1
if utils.pythonVersion() >= utils.PYTHON_2_4:
    zipMinArgs = 0
    
tryAddGlobal('zip', types.ListType, zipMinArgs, None)

tryAddGlobal('enumerate', types.TupleType, 1, 1, ['sequence'])
# sum() could also return float/long
tryAddGlobal('sum', types.IntType, 1, 2, ['start'])
# sorted() and reversed() always return an iterator  (FIXME: support iterator)
tryAddGlobal('sorted', Stack.TYPE_UNKNOWN, 1, 1)
tryAddGlobal('reversed', Stack.TYPE_UNKNOWN, 1, 1)

tryAddGlobal('all', BOOL, 1, 1)
tryAddGlobal('any', BOOL, 1, 1)

_STRING_METHODS = { 'capitalize': (types.StringType, 0, 0),
                    'center': (types.StringType, 1, 1),
                    'count': (types.IntType, 1, 1),
                    'encode': (types.StringType, 0, 2),
                    'endswith': (BOOL, 1, 3),
                    'expandtabs': (types.StringType, 0, 1),
                    'find': (types.IntType, 1, 3),
                    'index': (types.IntType, 1, 3),
                    'isalnum': (BOOL, 0, 0),
                    'isalpha': (BOOL, 0, 0),
                    'isdigit': (BOOL, 0, 0),
                    'islower': (BOOL, 0, 0),
                    'isspace': (BOOL, 0, 0),
                    'istitle': (BOOL, 0, 0),
                    'isupper': (BOOL, 0, 0),
                    'join': (types.StringType, 1, 1),
                    'ljust': (types.StringType, 1, 1),
                    'lower': (types.StringType, 0, 0),
                    'lstrip': (types.StringType, 0, 0),
                    'replace': (types.StringType, 2, 3),
                    'rfind': (types.IntType, 1, 3),
                    'rindex': (types.IntType, 1, 3),
                    'rjust': (types.StringType, 1, 1),
                    'rstrip': (types.StringType, 0, 0),
                    'split': (types.ListType, 0, 2),
                    'splitlines': (types.ListType, 0, 1),
                    'startswith': (BOOL, 1, 3),
                    'strip': (types.StringType, 0, 0),
                    'swapcase': (types.StringType, 0, 0),
                    'title': (types.StringType, 0, 0),
                    'translate': (types.StringType, 1, 2),
                    'upper': (types.StringType, 0, 0),
                  }

if utils.pythonVersion() >= utils.PYTHON_2_2 :
    _STRING_METHODS['decode'] = (types.UnicodeType, 0, 2)
    _STRING_METHODS['zfill'] = (types.StringType, 1, 1)

if utils.pythonVersion() >= utils.PYTHON_2_4:
    _STRING_METHODS['rsplit'] = (types.StringType, 0, 2)
    _STRING_METHODS['center'] = (types.StringType, 1, 2),
    _STRING_METHODS['ljust'] = (types.StringType, 1, 2),
    _STRING_METHODS['rjust'] = (types.StringType, 1, 2),

BUILTIN_METHODS = { types.DictType :
                    { 'clear': (types.NoneType, 0, 0),
                      'copy': (types.DictType, 0, 0),
                      'get': (Stack.TYPE_UNKNOWN, 1, 2),
                      'has_key': (BOOL, 1, 1),
                      'items': (types.ListType, 0, 0),
                      'keys': (types.ListType, 0, 0),
                      'popitem': (types.TupleType, 0, 0),
                      'setdefault': (Stack.TYPE_UNKNOWN, 1, 2),
                      'update': (types.NoneType, 1, 1),
                      'values': (types.ListType, 0, 0),
                    },
                    types.ListType :
                    { 'append': (types.NoneType, 1, 1),
                      'count': (types.IntType, 1, 1),
                      'extend': (types.NoneType, 1, 1),
                      'index': (types.IntType, 1, 1),
                      'insert': (types.NoneType, 2, 2),
                      'pop': (Stack.TYPE_UNKNOWN, 0, 1),
                      'remove': (types.NoneType, 1, 1),
                      'reverse': (types.NoneType, 0, 0),
                      'sort': (types.NoneType, 0, 1),
                    },
                    types.FileType :
                    { 'close': (types.NoneType, 0, 0),
                      'fileno': (types.IntType, 0, 0),
                      'flush': (types.NoneType, 0, 0),
                      'isatty': (BOOL, 0, 0),
                      'read': (types.StringType, 0, 1),
                      'readinto': (types.NoneType, 1, 1),
                      'readline': (types.StringType, 0, 1),
                      'readlines': (types.ListType, 0, 1),
                      'seek': (types.NoneType, 1, 2),
                      'tell': (types.IntType, 0, 0),
                      'truncate': (types.NoneType, 0, 1),
                      'write': (types.NoneType, 1, 1),
                      'writelines': (types.NoneType, 1, 1),
                      'xreadlines': (types.ListType, 0, 0),
                    },
                  }

if utils.pythonVersion() >= utils.PYTHON_2_4:
    GLOBAL_FUNC_INFO['set'] = (Stack.TYPE_UNKNOWN, 0, 1)
    GLOBAL_FUNC_INFO['frozenset'] = (Stack.TYPE_UNKNOWN, 0, 1)

    kwargs = ['cmp', 'key', 'reverse']
    BUILTIN_METHODS[types.ListType]['sort'] = (types.NoneType, 0, 3, kwargs)
    BUILTIN_METHODS[types.DictType]['update'] = (types.NoneType, 1, 1, [])

if hasattr({}, 'pop'):
    BUILTIN_METHODS[types.DictType]['pop'] = (Stack.TYPE_UNKNOWN, 1, 2)

if utils.pythonVersion() >= utils.PYTHON_2_5:
    _STRING_METHODS['partition'] = (types.TupleType, 1, 1)
    _STRING_METHODS['rpartition'] = (types.TupleType, 1, 1)

if utils.pythonVersion() >= utils.PYTHON_2_6:
    GLOBAL_FUNC_INFO['bin'] = (types.StringType, 1, 1)
    GLOBAL_FUNC_INFO['bytesarray'] = (bytearray, 0, 1)
    GLOBAL_FUNC_INFO['bytes'] = (bytes, 0, 1)
    GLOBAL_FUNC_INFO['format'] = (types.StringType, 1, 2)
    GLOBAL_FUNC_INFO['next'] = (Stack.TYPE_UNKNOWN, 1, 2)
    GLOBAL_FUNC_INFO['print'] = (types.NoneType, 0, None,
                                 ['sep', 'end', 'file'])

def _setupBuiltinMethods() :
    if utils.pythonVersion() >= utils.PYTHON_2_2 :
        PY22_DICT_METHODS = { 'iteritems': (types.ListType, 0, 0),
                              'iterkeys': (types.ListType, 0, 0),
                              'itervalues': (types.ListType, 0, 0),
                            }

        BUILTIN_METHODS[types.DictType].update(PY22_DICT_METHODS)

    try :
        BUILTIN_METHODS[types.ComplexType] = \
                                  { 'conjugate': (types.ComplexType, 0, 0), }
    except AttributeError :
        pass

    if len(dir('')) > 0 :
        BUILTIN_METHODS[types.StringType] = _STRING_METHODS

    try :
        BUILTIN_METHODS[types.UnicodeType] = _STRING_METHODS
    except AttributeError :
        pass

_setupBuiltinMethods()

MUTABLE_TYPES = (types.ListType, types.DictType, types.InstanceType,)

# identifiers which will become a keyword in a future version
FUTURE_KEYWORDS = { 'yield': '2.2', 'with': '2.5', 'as': '2.5' }

METHODLESS_OBJECTS = { types.NoneType : None, types.IntType : None,
                       types.LongType : None, types.FloatType : None,
                       types.BufferType : None, types.TupleType : None,
                       types.EllipsisType : None,
                     }

def _setupBuiltinAttrs() :
    item = Stack.Item(None, None)
    BUILTIN_ATTRS[types.MethodType] = dir(item.__init__)
    del item

    if utils.pythonVersion() >= utils.PYTHON_2_2 :
        # FIXME: I'm sure more types need to be added here
        BUILTIN_ATTRS[types.StringType] = dir(''.__class__)
        BUILTIN_ATTRS[types.ListType] = dir([].__class__)
        BUILTIN_ATTRS[types.DictType] = dir({}.__class__)

    try :
        import warnings
        _MSG = "xrange object's 'start', 'stop' and 'step' attributes are deprecated"
        warnings.filterwarnings('ignore', _MSG)
        del warnings, _MSG
    except (ImportError, AssertionError):
        pass
    BUILTIN_ATTRS[types.XRangeType] = dir(xrange(0))

    try: BUILTIN_ATTRS[types.ComplexType] = dir(complex(0, 1))
    except: pass

    try: BUILTIN_ATTRS[types.UnicodeType] = dir(unicode(''))
    except: pass

    try: BUILTIN_ATTRS[types.CodeType] = dir(_setupBuiltinAttrs.func_code)
    except: pass

    try: BUILTIN_ATTRS[types.FileType] = dir(sys.__stdin__)
    except: pass

    try:
        raise TypeError
    except TypeError :
        try:
            tb = sys.exc_info()[2]
            BUILTIN_ATTRS[types.TracebackType] = dir(tb)
            BUILTIN_ATTRS[types.FrameType] = dir(tb.tb_frame)
        except:
            pass
        tb = None

BUILTIN_ATTRS = { types.StringType : dir(''),
                  types.TypeType : dir(type(type)),
                  types.ListType : dir([]),
                  types.DictType : dir({}),
                  types.FunctionType : dir(_setupBuiltinAttrs),
                  types.BuiltinFunctionType : dir(len),
                  types.BuiltinMethodType : dir([].append),
                  types.ClassType : dir(Stack.Item),
                  types.UnboundMethodType : dir(Stack.Item.__init__),
                  types.LambdaType : dir(lambda: None),
                  types.SliceType : dir(slice(0)),
                }

# have to setup the rest this way to support different versions of Python
_setupBuiltinAttrs()

PENDING_DEPRECATED_MODULES = { 'string': None, 'types': None,
                             }
DEPRECATED_MODULES = { 'FCNTL': 'fcntl', 'gopherlib': None,
                       'macfs': 'Carbon.File or Carbon.Folder',
                       'posixfile': 'fcntl', 'pre': None, 'regsub': 're',
                       'statcache': 'os.stat()',
                       'stringold': None, 'tzparse': None,
                       'TERMIOS': 'termios', 'whrandom':'random',
                       'xmllib': 'xml.sax',

                       # C Modules
                       'mpz': None, 'pcre': None, 'pypcre': None,
                       'rgbimg': None, 'strop': None, 'xreadlines': 'file',
                     }
DEPRECATED_ATTRS = { 'array.read': None, 'array.write': None,
                     'operator.isCallable': None,
                     'operator.sequenceIncludes': None,
                     'pty.master_open': None, 'pty.slave_open': None,
                     'random.stdgamma': 'random.gammavariate',
                     'rfc822.AddrlistClass': 'rfc822.AddressList',
                     'string.atof': None, 'string.atoi': None,
                     'string.atol': None, 'string.zfill': None,
                     'sys.exc_traceback': None, 'sys.exit_thread': None,
                     'tempfile.mktemp': None, 'tempfile.template': None,
                   }

# FIXME: can't check these right now, maybe later
DEPRECATED_METHODS = {
                       'email.Message.get_type': 'email.Message.get_content_type',
                       'email.Message.get_subtype': 'email.Message.get_content_subtype',
                       'email.Message.get_main_type': 'email.Message.get_content_maintype',
                       'htmllib.HTMLParser.do_nextid': None,
                       'pstats.Stats.ignore': None,
                       'random.Random.cunifvariate': None,
                       'random.Random.stdgamma': 'Random.gammavariate',
                     }

_OS_AND_POSIX_FUNCS = { 'tempnam': None, 'tmpnam': None }
SECURITY_FUNCS = { 'os' : _OS_AND_POSIX_FUNCS, 'posix': _OS_AND_POSIX_FUNCS }

SPECIAL_METHODS = {
    '__call__': None,                   # any number > 1
    '__cmp__': 2,
    '__coerce__': 2,
    '__contains__': 2,
    '__del__': 1,
    '__hash__': 1,
    '__iter__': 1,
    '__len__': 1,
    '__new__': None,			# new-style class constructor
    '__nonzero__': 1,

    '__hex__': 1,
    '__oct__': 1,
    '__repr__': 1,
    '__str__': 1,

    '__invert__': 1,	'__neg__': 1,	'__pos__': 1,     '__abs__': 1,     
    '__complex__': 1,	'__int__': 1,	'__long__': 1,    '__float__': 1,
    '__unicode__': 1,

    '__eq__': 2,	'__ne__': 2,
    '__ge__': 2,	'__gt__': 2,
    '__le__': 2,	'__lt__': 2,

    '__getattribute__': 2,	# only in new-style classes
    '__get__': 3,		'__set__': 3,		'__delete__': 2,
    '__getattr__': 2,		'__setattr__': 3,	'__delattr__': 2,
    '__getitem__': 2,		'__setitem__': 3,	'__delitem__': 2,
    '__getslice__': 3,		'__setslice__': 4,	'__delslice__': 3,
    # getslice is deprecated

    '__add__': 2,	'__radd__': 2,		'__iadd__': 2,    
    '__sub__': 2,	'__rsub__': 2,		'__isub__': 2,
    '__mul__': 2,	'__rmul__': 2,		'__imul__': 2,    
    '__div__': 2,	'__rdiv__': 2,		'__idiv__': 2,
    #  __pow__: 2 or 3                           __ipow__: 2 or 3
    '__pow__': 3,	'__rpow__': 2,		'__ipow__': 3,
    '__truediv__': 2,	'__rtruediv__': 2,	'__itruediv__': 2,	
    '__floordiv__': 2,	'__rfloordiv__': 2,	'__ifloordiv__': 2,	
    '__mod__': 2,	'__rmod__': 2,		'__imod__': 2,    
    '__divmod__': 2,	'__rdivmod__': 2,	# no inplace op for divmod()

    '__lshift__': 2,	'__rlshift__': 2,	'__ilshift__': 2,
    '__rshift__': 2,	'__rrshift__': 2,	'__irshift__': 2, 

    '__and__': 2,	'__rand__': 2,		'__iand__': 2,
    '__xor__': 2,	'__rxor__': 2,		'__ixor__': 2,
    '__or__': 2,	'__ror__': 2,		'__ior__': 2,

    # these are related to pickling 
    '__getstate__': 1,		'__setstate__': 2,
    '__copy__': 1,		'__deepcopy__': 2,
    '__getinitargs__': 1,	'__getnewargs__': 1,
    '__reduce__': 1,		'__reduce_ex__': 2,
    }

if utils.pythonVersion() >= utils.PYTHON_2_5:
    SPECIAL_METHODS['__enter__'] = 1
    SPECIAL_METHODS['__exit__'] = 4

NEW_STYLE_CLASS_METHODS = ['__getattribute__', '__set__', '__get__', '__delete__']
