# Python test set -- built-in functions

import unittest
from operator import neg
import sys

class Squares:

    def __init__(self, max):
        self.max = max
        self.sofar = []

    def __len__(self): return len(self.sofar)

    def __getitem__(self, i):
        if not 0 <= i < self.max: raise IndexError
        n = len(self.sofar)
        while n <= i:
            self.sofar.append(n*n)
            n += 1
        return self.sofar[i]

class TestFailingIter:
    def __iter__(self):
        raise RuntimeError

class BuiltinTest(unittest.TestCase):

    def test_getattr(self):
        import sys
        self.assertTrue(getattr(sys, 'maxint') is sys.maxint)
        self.assertRaises(TypeError, getattr, sys, 1)
        self.assertRaises(TypeError, getattr)

    def test_hasattr(self):
        import sys
        self.assertTrue(hasattr(sys, 'maxint'))
        self.assertRaises(TypeError, hasattr, sys, 1)
        self.assertRaises(TypeError, hasattr)

        class A:
            def __init__(self):
                self.undef_self = None

            def undefxz(self):
                return None

            def __getattr__(self, name):
                if name == 'undef':
                    return None
                if name == 'one':
                    return 1
                raise AttributeError

        class NoGAtt:
            def __init__(self):
                self.undef_self = None

            def undefxz(self):
                return None
        a = A()
        b = NoGAtt()
        self.assertTrue(hasattr(a, 'undefxz'))
        self.assertTrue(hasattr(a, 'undef_self'))
        self.assertTrue(hasattr(a, 'one'))
        self.assertTrue(hasattr(a, 'undef'))
        self.assertFalse(hasattr(a, "Robyn"))
        self.assertTrue(hasattr(b, 'undefxz'))
        self.assertTrue(hasattr(b, 'undef_self'))
        self.assertFalse(hasattr(b, 'one'))
        self.assertFalse(hasattr(b, 'undef'))
        self.assertFalse(hasattr(a, "Robyn"))

    def test_setattr(self):
        setattr(sys, 'spam', 1)
        self.assertEqual(sys.spam, 1)
        self.assertRaises(TypeError, setattr, sys, 1, 'spam')
        self.assertRaises(AttributeError, setattr, 1, 'spam', 9)
        self.assertRaises(TypeError, setattr)
        for builtin_type in (int, float, Exception, object, type, super):
            self.assertRaises(TypeError, setattr, builtin_type, 'foo', 'bar')
            with self.assertRaises(TypeError):
                builtin_type.foo = 'bar'
        

    def test_delattr(self):
        class NoName:
            def color(self):
                return "green"
        x = NoName()
        y = NoName
        setattr(NoName, "shape", "square")
        self.assertEqual(getattr(NoName, "shape"), "square")
        self.assertTrue(hasattr(NoName, "shape"))
        self.assertEqual(getattr(NoName(), "shape"), "square")
        self.assertTrue(hasattr(NoName(), "shape"))
        self.assertRaises(AttributeError, delattr, NoName(), "shape")
        self.assertEqual(getattr(x, "shape"), "square")
        self.assertEqual(getattr(y, "shape"), "square")
        setattr(y, "shape", "circle")
        setattr(x, "shape", "square")
        self.assertEqual(getattr(NoName, "shape"), "circle")
        self.assertEqual(getattr(NoName(), "shape"), "circle")
        self.assertEqual(getattr(x, "shape"), "square")
        self.assertEqual(getattr(y, "shape"), "circle")
        delattr(NoName, "shape")
        self.assertEqual(getattr(x, "shape"), "square")
        self.assertRaises(AttributeError, delattr, y, "shape")
        self.assertRaises(AttributeError, delattr, NoName(), "shape")
        self.assertRaises(AttributeError, delattr, NoName, "shape")
        self.assertEqual(getattr(x, "color")(), "green")
        self.assertEqual(getattr(NoName(), "color")(), "green")
        self.assertEqual(getattr(x, "color")(), "green")
        setattr(x, "color", "red")
        self.assertEqual(x.color, "red")
        delattr(NoName, "color")
        self.assertEqual(x.color, "red")
        self.assertRaises(AttributeError, delattr, NoName, "color")
        self.assertEqual(x.color, "red")
        self.assertRaises(AttributeError, delattr, NoName, "color")
        self.assertRaises(TypeError, delattr, list, "append")
        self.assertRaises(TypeError, setattr, dict, "k", "v")

    def test_dir_subclasses(self):
        class Base:
            def method1(self):
                pass

        class Sub(Base):
            def method2(self):
                pass

        self.assertIn("method1", dir(Sub))
        self.assertIn("method2", dir(Sub))
        sub = Sub()
        self.assertIn("method1", dir(sub))
        self.assertIn("method2", dir(sub))

    def test_all(self):
        self.assertEqual(all([2, 4, 6]), True)
        self.assertEqual(all([2, None, 6]), False)
        # self.assertRaises(RuntimeError, all, [2, TestFailingBool(), 6])
        self.assertRaises(RuntimeError, all, TestFailingIter())
        self.assertRaises(TypeError, all, 10)               # Non-iterable
        self.assertRaises(TypeError, all)                   # No args
        self.assertRaises(TypeError, all, [2, 4, 6], [])    # Too many args
        self.assertEqual(all([]), True)                     # Empty iterator
        # self.assertEqual(all([0, TestFailingBool()]), False)# Short-circuit
        S = [50, 60]
        self.assertEqual(all(x > 42 for x in S), True)
        S = [50, 40, 60]
        self.assertEqual(all(x > 42 for x in S), False)

    def test_any(self):
        self.assertEqual(any([None, None, None]), False)
        self.assertEqual(any([None, 4, None]), True)
        # self.assertRaises(RuntimeError, any, [None, TestFailingBool(), 6])
        self.assertRaises(RuntimeError, any, TestFailingIter())
        self.assertRaises(TypeError, any, 10)               # Non-iterable
        self.assertRaises(TypeError, any)                   # No args
        self.assertRaises(TypeError, any, [2, 4, 6], [])    # Too many args
        self.assertEqual(any([]), False)                    # Empty iterator
        # self.assertEqual(any([1, TestFailingBool()]), True) # Short-circuit
        S = [40, 60, 30]
        self.assertEqual(any(x > 42 for x in S), True)
        S = [10, 20, 30]
        self.assertEqual(any(x > 42 for x in S), False)

    def test_sum(self):
        self.assertEqual(sum([]), 0)
        self.assertEqual(sum(list(range(2,8))), 27)
        # self.assertEqual(sum(iter(list(range(2,8)))), 27)
        self.assertEqual(sum(Squares(10)), 285)
        # self.assertEqual(sum(iter(Squares(10))), 285)
        self.assertEqual(sum([[1], [2], [3]], []), [1, 2, 3])

        self.assertRaises(TypeError, sum)
        self.assertRaises(TypeError, sum, 42)
        self.assertRaises(TypeError, sum, ['a', 'b', 'c'])
        self.assertRaises(TypeError, sum, ['a', 'b', 'c'], '')
        self.assertRaises(TypeError, sum, [[1], [2], [3]])
        self.assertRaises(TypeError, sum, [{2:3}])

        self.assertRaises(TypeError, sum, [{2:3}]*2, {2:3})

        class BadSeq:
            def __getitem__(self, index):
                raise ValueError
        self.assertRaises(ValueError, sum, BadSeq())


        empty = []
        sum(([x] for x in range(10)), empty)
        self.assertEqual(empty, [])

    def test_zip(self):
        a = (1, 2, 3)
        b = (4, 5, 6)
        t = [(1, 4), (2, 5), (3, 6)]
        self.assertEqual(zip(a, b), t)
        b = [4, 5, 6]
        self.assertEqual(zip(a, b), t)
        b = (4, 5, 6, 7)
        self.assertEqual(zip(a, b), t)
        class I:
            def __getitem__(self, i):
                if i < 0 or i > 2: raise IndexError
                return i + 4
        self.assertEqual(zip(a, I()), t)
        self.assertEqual(zip(), [])
        self.assertEqual(zip(*[]), [])
        self.assertRaises(TypeError, zip, None)
        class G:
            pass
        self.assertRaises(TypeError, zip, a, G())

        # Make sure zip doesn't try to allocate a billion elements for the
        # result list when one of its arguments doesn't say how long it is.
        # A MemoryError is the most likely failure mode.
        class SequenceWithoutALength:
            def __getitem__(self, i):
                if i == 5:
                    raise IndexError
                else:
                    return i
        self.assertEqual(
            #zip(SequenceWithoutALength(), xrange(2**30)),
            zip(SequenceWithoutALength(), xrange(2**8)), # no working xrange
            list(enumerate(range(5)))
        )

        class BadSeq:
            def __getitem__(self, i):
                if i == 5:
                    raise ValueError
                else:
                    return i
        self.assertRaises(ValueError, zip, BadSeq(), BadSeq())

    def test_map(self):
        self.assertEqual(
            map(None, 'hello world'),
            ['h','e','l','l','o',' ','w','o','r','l','d']
        )
        self.assertEqual(
            map(None, 'abcd', 'efg'),
            [('a', 'e'), ('b', 'f'), ('c', 'g'), ('d', None)]
        )
        self.assertEqual(
            map(None, range(10)),
            [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
        )
        self.assertEqual(
            map(lambda x: x*x, range(1,4)),
            [1, 4, 9]
        )
        try:
            from math import sqrt
        except ImportError:
            def sqrt(x):
                return pow(x, 0.5)
        self.assertEqual(
            map(lambda x: map(sqrt,x), [[16, 4], [81, 9]]),
            [[4.0, 2.0], [9.0, 3.0]]
        )
        self.assertEqual(
            map(lambda x, y: x+y, [1,3,2], [9,1,4]),
            [10, 4, 6]
        )

        def plus(*v):
            accu = 0
            for i in v: accu = accu + i
            return accu
        self.assertEqual(
            map(plus, [1, 3, 7]),
            [1, 3, 7]
        )
        self.assertEqual(
            map(plus, [1, 3, 7], [4, 9, 2]),
            [1+4, 3+9, 7+2]
        )
        self.assertEqual(
            map(plus, [1, 3, 7], [4, 9, 2], [1, 1, 0]),
            [1+4+1, 3+9+1, 7+2+0]
        )
        self.assertEqual(
            map(None, Squares(10)),
            [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
        )
        self.assertEqual(
            map(int, Squares(10)),
            [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
        )
        self.assertEqual(
            map(None, Squares(3), Squares(2)),
            [(0,0), (1,1), (4,None)]
        )
        self.assertEqual(
            map(max, Squares(3), Squares(2)),
            [0, 1, 4]
        )
        self.assertRaises(TypeError, map)
        self.assertRaises(TypeError, map, lambda x: x, 42)
        self.assertEqual(map(None, [42]), [42])
        class BadSeq:
            def __getitem__(self, index):
                raise ValueError
        self.assertRaises(ValueError, map, lambda x: x, BadSeq())
        def badfunc(x):
            raise RuntimeError
        self.assertRaises(RuntimeError, map, badfunc, range(5))

    def test_abs(self):
        class TestAbs:

            def __init__(self):
                self.foo = -3

            def __abs__(self):
                return -self.foo

        bar = TestAbs()
        self.assertEqual(abs(bar), 3)
        self.assertEqual(abs(-3), 3)

    def test_reversed(self):
        class TestReversed:
            def __reversed__(self):
                return [5, 4, 3, 2, 1]

        self.assertEqual(list(reversed([1, 2, 3])), [3, 2, 1])

        obj = TestReversed()
        self.assertEqual(reversed(obj), [5, 4, 3, 2, 1])

    def test_reduce(self):
        add = lambda x, y: x+y
        self.assertEqual(reduce(add, ['a', 'b', 'c'], ''), 'abc')
        self.assertEqual(
            reduce(add, [['a', 'c'], [], ['d', 'w']], []),
            ['a','c','d','w']
        )
        self.assertEqual(reduce(lambda x, y: x*y, range(2,8), 1), 5040)
        self.assertEqual(
            reduce(lambda x, y: x*y, range(2,21), 1L),
            2432902008176640000L
        )
        self.assertEqual(reduce(add, Squares(10)), 285)
        self.assertEqual(reduce(add, Squares(10), 0), 285)
        self.assertEqual(reduce(add, Squares(0), 0), 0)
        self.assertRaises(TypeError, reduce)
        self.assertRaises(TypeError, reduce, 42)
        self.assertRaises(TypeError, reduce, 42, 42)
        self.assertRaises(TypeError, reduce, 42, 42, 42)
        self.assertRaises(TypeError, reduce, None, range(5))
        self.assertRaises(TypeError, reduce, add, 42)
        self.assertEqual(reduce(42, "1"), "1") # func is never called with one item
        self.assertEqual(reduce(42, "", "1"), "1") # func is never called with one item
        self.assertRaises(TypeError, reduce, 42, (42, 42))
        self.assertRaises(TypeError, reduce, add, []) # arg 2 must not be empty sequence with no initial value
        self.assertRaises(TypeError, reduce, add, "")
        self.assertRaises(TypeError, reduce, add, ())
        self.assertEqual(reduce(add, [], None), None)
        self.assertEqual(reduce(add, [], 42), 42)

        class BadSeq:
            def __getitem__(self, index):
                raise ValueError
        self.assertRaises(ValueError, reduce, 42, BadSeq())

    def test_filter(self):
        self.assertEqual(filter(lambda c: 'a' <= c <= 'z', 'Hello World'), 'elloorld')
        self.assertEqual(filter(None, [1, 'hello', [], [3], '', None, 9, 0]), [1, 'hello', [3], 9])
        self.assertEqual(filter(lambda x: x > 0, [1, -3, 9, 0, 2]), [1, 9, 2])
        self.assertEqual(filter(None, Squares(10)), [1, 4, 9, 16, 25, 36, 49, 64, 81])
        self.assertEqual(filter(lambda x: x%2, Squares(10)), [1, 9, 25, 49, 81])
        def identity(item):
            return 1
        filter(identity, Squares(5))
        self.assertRaises(TypeError, filter)

        class BadSeq(object):
            def __getitem__(self, index):
                if index<4:
                    return 42
                raise ValueError
        self.assertRaises(ValueError, filter, lambda x: x, BadSeq())
        def badfunc():
            pass
        self.assertRaises(TypeError, filter, badfunc, range(5))

        # test bltinmodule.c::filtertuple()
        self.assertEqual(filter(None, (1, 2)), (1, 2))
        self.assertEqual(filter(lambda x: x>=3, (1, 2, 3, 4)), (3, 4))
        self.assertRaises(TypeError, filter, 42, (1, 2))

        # test bltinmodule.c::filterstring()
        self.assertEqual(filter(None, "12"), "12")
        self.assertEqual(filter(lambda x: x>="3", "1234"), "34")
        self.assertRaises(TypeError, filter, 42, "12")

        # class badstr(str):
        #     def __getitem__(self, index):
        #         raise ValueError
        # self.assertRaises(ValueError, filter, lambda x: x >="3", badstr("1234"))

        # class badstr2(str):
        #     def __getitem__(self, index):
        #         return 42
        # self.assertRaises(TypeError, filter, lambda x: x >=42, badstr2("1234"))

        # class weirdstr(str):
        #     def __getitem__(self, index):
        #         return weirdstr(2*str.__getitem__(self, index))
        # self.assertEqual(filter(lambda x: x>="33", weirdstr("1234")), "3344")

        # class shiftstr(str):
        #     def __getitem__(self, index):
        #         return chr(ord(str.__getitem__(self, index))+1)
        # self.assertEqual(filter(lambda x: x>="3", shiftstr("1234")), "345")

    def test_callable(self):
        self.assertTrue(callable(len))                            # builtin
        self.assertFalse(callable("a"))
        self.assertTrue(callable(callable))                       # builtin
        self.assertTrue(callable(lambda x, y: x + y))             # python lambda
        def f(): pass
        self.assertTrue(callable(f))                              # python func
        class C1(object):
            def meth(self): pass
        self.assertTrue(callable(C1))                               # class
        c = C1()
        self.assertTrue(callable(c.meth))                           # method (instance)
        # __call__ is looked up on the class, not the instance
        c.__call__ = None
        self.assertFalse(callable(c))                               # class instance without __call__ in class
        class C2(object):
            def __call__(self): pass
        c2 = C2()
        self.assertTrue(callable(c2))                               # class instance with __call__ in class
        self.assertFalse(callable(False))                                       # bool
        self.assertFalse(callable(["this is a list"]))                          # list        
        self.assertFalse(callable(["this is a list with a function", f]))       # list containing function
        self.assertFalse(callable({}))                                          # dict
        self.assertFalse(callable(None))                                        # nonetype
        self.assertFalse(callable(1))                                           # number
        self.assertTrue(callable(float))                                        # float builtin
        self.assertFalse(callable(float()))                                     # function call
        self.assertFalse(callable(float(1)))                                    # float type
        self.assertFalse(callable("+"))                                         # string containing symbol
        self.assertFalse(callable(4+7))                                         # expression
        self.assertFalse(callable(enumerate(['a', 'b', 'c', 'd'])))             # list created by callable function
        self.assertFalse(callable(()))                                          # tuple                                        
        # generators are callable
        def squares(n):
            '''Yields the squares from 0 to n-1 squared.'''
            for i in range(n):
                yield i * i
        self.assertTrue(callable(squares))                                      # generator
        class SuperClass(object): #superclass
            def getName(self):
                raise NotImplementedError
        class LittleClass(SuperClass): #subclass/ inherited from SuperClass
                def __call__(self):
                    return "LittleClass"
        big = SuperClass()
        big.__call__ = "call me super"
        self.assertFalse(callable(big))                                             # checking callable by class (not instances)
        class_inst = LittleClass()                                                  # class instances are not callable unless they have a .__call__ method
        self.assertTrue(callable(class_inst)) 
        #### nested classes
        class Outer(object):
            def __init__(self):
                self.y = 0

            class Inner(object):
                def __init__(self):
                    self.x = 1
        self.assertTrue(callable(Outer.Inner))                                 # function object
        self.assertTrue(callable(Outer.Inner.__init__))
    
    def test_next(self):
        itera = iter([1,2,3])                                 # iterator
        self.assertEqual(next(itera), 1)
        self.assertEqual(next(itera), 2)
        self.assertEqual(next(itera), 3)
        self.assertEqual(next(itera, "stop please"), "stop please") # test default
        self.assertRaises(StopIteration, next, itera)               # causes StopIteration
        self.assertRaises(TypeError, next, [1,2,3,4])               # type errors "not an iterator" : list
        self.assertRaises(TypeError, next, (1,2,3,4))               # tuple
        self.assertRaises(TypeError, next, "hello")                 # string
        self.assertEqual(next(iter("hello")), "h")                  # iterator from a string
        self.assertRaises(TypeError, next, False)                   # Type Error: bool object is not an iterator
        self.assertRaises(TypeError, next, {1:2})                   # TypeError: dict object is not an iterator
        self.assertRaises(TypeError, next, 1)                       # TypeError: int object is not an iterator
        class Noniter:
            def __init__(self, num):
                self.mynum = num
        noniterb = Noniter(1)                                           # "class instance without __iter__ method"   
        self.assertRaises(TypeError, next, noniterb)                    # TypeError: instance has no next() method
        self.assertRaises(TypeError, next, noniterb, "defaultreturn")   # class instance without __iter__ method, has default
        iterc = Noniter(2)
        iterc.__iter__ = iter([1,2,3])                                  # class instance with __iter__ method
        self.assertRaises(TypeError, next, iterc)                       # TypeError: instance has no next() method                   # stop iteration
        self.assertEqual(next(iterc.__iter__), 1)                 # iterator class without __iter__, instance with __iter__... should raise StopIteration error
        self.assertEqual(next(iterc.__iter__), 2)
        self.assertEqual(next(iterc.__iter__), 3)
        self.assertRaises(StopIteration, next, iterc.__iter__)
        class Iterb:
            def __init__(self, lst):
                self.lst = lst
                self.iter = iter(self.lst)

            def __iter__(self):
                return self.iter
        iterb = Iterb((1, 2))
        self.assertRaises(TypeError, next, iterb)                  # TypeError: instance has no next() method
        self.assertEqual(next(iterb.__iter__()), 1)                 # class with __iter__ method
        self.assertEqual(next(iterb.__iter__()), 2)  
        self.assertRaises(StopIteration, next, iterb.__iter__())
        self.assertRaises(StopIteration, next, iterb.__iter__())
        iterstr = Iterb("str")
        self.assertEqual(next(iterstr.__iter__(), "ing"), "s")      # iterator class with string and default
        self.assertEqual(next(iterstr.__iter__(), "ing"), "t")
        self.assertEqual(next(iterstr.__iter__(), "ing"), "r")
        self.assertEqual(next(iterstr.__iter__(), "ing"), "ing")    # class instance with __iter__ method, testing default
        self.assertEqual(next(iterstr.__iter__(), "ing"), "ing")
        self.assertEqual(next(iterstr.__iter__(), "ing"), "ing")
        self.assertRaises(StopIteration, next, iterb.__iter__())    # class instance with __iter__ method, testing stop/no default
        spellcow = iter("cow")                                      # tuple iterator
        self.assertEqual(next(spellcow, "moo"), "c")                # sring, with defaul
        self.assertEqual(next(spellcow, "moo"), "o")
        self.assertEqual(next(spellcow, "moo"), "w")
        self.assertEqual(next(spellcow, "moo"), "moo")
        iterd = iter([])                                            # empty iterator
        self.assertRaises(StopIteration, next, iterd)

        class HasNext:
            def __init__(self, num):
                self.mynum = num

            def next(self):
                return "this is the next value"
        hasnext1 = HasNext(123)
        self.assertEqual(next(hasnext1), "this is the next value")  # has a "next" method

        class NoNext:
            def __iter__(self):
                return self
        NoNextinst = NoNext()
        self.assertRaises(TypeError, next, NoNextinst.__iter__)     # TypeError: instancemethod object is not an iterator
        self.assertRaises(TypeError, next, NoNextinst.__iter__())

        it = iter(range(2))                                         # cpython testing
        self.assertEqual(next(it), 0)
        self.assertEqual(next(it), 1)
        self.assertRaises(StopIteration, next, it)
        self.assertRaises(StopIteration, next, it)
        self.assertEqual(next(it, 42), 42)

        class Iter:
            def __iter__(self):
                return self

            def next(self):
                raise StopIteration

        it = iter(Iter())
        self.assertEqual(next(it, 42), 42)
        self.assertRaises(StopIteration, next, it)

        def gen():
            yield 1
            return

        it = gen()
        self.assertEqual(next(it), 1)
        self.assertRaises(StopIteration, next, it)
        self.assertEqual(next(it, 42), 42)

    def test_skulpt_bugs(self):
        # bug where sum should accept non numbers types
        self.assertEqual(sum([complex(1,1), 1]), complex(2, 1))
        class A:
            def __add__(self, other):
                return 3
            def __radd__(self, other):
                return 3
        self.assertEqual(sum([A(), A()]), 3)
        self.assertEqual(sum([1,2,3], A()), 8)

if __name__ == "__main__":
    unittest.main()
