# Test case for property
# more tests are in test_descr

import sys
import unittest
import time

class PropertyBase(Exception):
    pass

class PropertyGet(PropertyBase):
    pass

class PropertySet(PropertyBase):
    pass

class PropertyDel(PropertyBase):
    pass

class BaseClass(object):
    def __init__(self):
        self._spam = 5

    @property
    def spam(self):
        """BaseClass.getter"""
        return self._spam

    @spam.setter
    def spam(self, value):
        self._spam = value

    @spam.deleter
    def spam(self):
        pass
        #del self._spam

class SubClass(BaseClass):

    @BaseClass.spam.getter
    def spam(self):
        """SubClass.getter"""
        raise PropertyGet(self._spam)

    @spam.setter
    def spam(self, value):
        raise PropertySet(self._spam)

    @spam.deleter
    def spam(self):
        raise PropertyDel(self._spam)

class PropertyDocBase(object):
    _spam = 1
    def _get_spam(self):
        return self._spam
    spam = property(_get_spam, doc="spam spam spam")

class PropertyDocSub(PropertyDocBase):
    @PropertyDocBase.spam.getter
    def spam(self):
        """The decorator does not use this doc string"""
        return self._spam

class PropertySubNewGetter(BaseClass):
    @BaseClass.spam.getter
    def spam(self):
        """new docstring"""
        return 5

class PropertyNewGetter(object):
    @property
    def spam(self):
        """original docstring"""
        return 1
    @spam.getter
    def spam(self):
        """new docstring"""
        return 8

class PropertyTests(unittest.TestCase):
    def test_property_decorator_baseclass(self):
        # see #1620
        base = BaseClass()
        self.assertEqual(base.spam, 5)
        self.assertEqual(base._spam, 5)
        base.spam = 10
        self.assertEqual(base.spam, 10)
        self.assertEqual(base._spam, 10)
        # delattr(base, "spam")
        # self.assertTrue(not hasattr(base, "spam"))
        # self.assertTrue(not hasattr(base, "_spam"))
        base.spam = 20
        self.assertEqual(base.spam, 20)
        self.assertEqual(base._spam, 20)

    def test_property_decorator_subclass(self):
        # see #1620
        sub = SubClass()
        self.assertRaises(PropertyGet, getattr, sub, "spam")
        self.assertRaises(PropertySet, setattr, sub, "spam", None)
        # self.assertRaises(PropertyDel, delattr, sub, "spam")

    # def test_property_decorator_subclass_doc(self):
    #     sub = SubClass()
    #     self.assertEqual(sub.__class__.spam.__doc__, "SubClass.getter")
    #
    # def test_property_decorator_baseclass_doc(self):
    #     base = BaseClass()
    #     self.assertEqual(base.__class__.spam.__doc__, "BaseClass.getter")

    # def test_property_decorator_doc(self):
    #     base = PropertyDocBase()
    #     sub = PropertyDocSub()
    #     self.assertEqual(base.__class__.spam.__doc__, "spam spam spam")
    #     self.assertEqual(sub.__class__.spam.__doc__, "spam spam spam")

    # def test_property_getter_doc_override(self):
    #     newgettersub = PropertySubNewGetter()
    #     self.assertEqual(newgettersub.spam, 5)
    #     self.assertEqual(newgettersub.__class__.spam.__doc__, "new docstring")
    #     newgetter = PropertyNewGetter()
    #     self.assertEqual(newgetter.spam, 8)
    #     self.assertEqual(newgetter.__class__.spam.__doc__, "new docstring")

    # def test_property___isabstractmethod__descriptor(self):
    #     for val in (True, False, [], [1], '', '1'):
    #         class C(object):
    #             def foo(self):
    #                 pass
    #             foo.__isabstractmethod__ = val
    #             foo = property(foo)
    #         self.assertIs(C.foo.__isabstractmethod__, bool(val))
    #
    #     # check that the property's __isabstractmethod__ descriptor does the
    #     # right thing when presented with a value that fails truth testing:
    #     class NotBool(object):
    #         def __bool__(self):
    #             raise ValueError()
    #         __len__ = __bool__
    #     with self.assertRaises(ValueError):
    #         class C(object):
    #             def foo(self):
    #                 pass
    #             foo.__isabstractmethod__ = NotBool()
    #             foo = property(foo)
    #         C.foo.__isabstractmethod__

    def test_property_builtin_doc_writable(self):
        p = property(doc='basic')
        self.assertEqual(p.__doc__, 'basic')
        p.__doc__ = 'extended'
        self.assertEqual(p.__doc__, 'extended')

    # def test_property_decorator_doc_writable(self):
    #     class PropertyWritableDoc(object):
    #
    #         @property
    #         def spam(self):
    #             """Eggs"""
    #             return "eggs"
    #
    #     sub = PropertyWritableDoc()
    #     self.assertEqual(sub.__class__.spam.__doc__, 'Eggs')
    #     sub.__class__.spam.__doc__ = 'Spam'
    #     self.assertEqual(sub.__class__.spam.__doc__, 'Spam')

# Issue 5890: subclasses of property do not preserve method __doc__ strings
class PropertySub(property):
    """This is a subclass of property"""

class PropertySubSlots(property):
    """This is a subclass of property that defines __slots__"""
    __slots__ = ()

class Foo(object):
    def __init__(self): self._spam = 1
    @PropertySub
    def spam(self):
        """spam wrapped in property subclass"""
        return self._spam
    @spam.setter
    def spam(self, value):
        """this docstring is ignored"""
        self._spam = value

class FooSub(Foo):
    @Foo.spam.setter
    def spam(self, value):
        """another ignored docstring"""
        self._spam = 'eggs'

class PropertySubclassTests(unittest.TestCase):

    # def test_slots_docstring_copy_exception(self):
    #     try:
    #         class Foo(object):
    #             @PropertySubSlots
    #             def spam(self):
    #                 """Trying to copy this docstring will raise an exception"""
    #                 return 1
    #     except AttributeError:
    #         pass
    #     else:
    #         raise Exception("AttributeError not raised")
    #
    # def test_docstring_copy(self):
    #     class Foo(object):
    #         @PropertySub
    #         def spam(self):
    #             """spam wrapped in property subclass"""
    #             return 1
    #     self.assertEqual(
    #         Foo.spam.__doc__,
    #         "spam wrapped in property subclass")

    def test_property_setter_copies_getter_docstring(self):
        foo = Foo()
        self.assertEqual(foo.spam, 1)
        foo.spam = 2
        self.assertEqual(foo.spam, 2)
        # self.assertEqual(
        #     Foo.spam.__doc__,
        #     "spam wrapped in property subclass")

        foosub = FooSub()
        self.assertEqual(foosub.spam, 1)
        foosub.spam = 7
        self.assertEqual(foosub.spam, 'eggs')
        # self.assertEqual(
        #     FooSub.spam.__doc__,
        #     "spam wrapped in property subclass")

    # def test_property_new_getter_new_docstring(self):
    #
    #     class Foo(object):
    #         @PropertySub
    #         def spam(self):
    #             """a docstring"""
    #             return 1
    #         @spam.getter
    #         def spam(self):
    #             """a new docstring"""
    #             return 2
    #     self.assertEqual(Foo.spam.__doc__, "a new docstring")
    #     class FooBase(object):
    #         @PropertySub
    #         def spam(self):
    #             """a docstring"""
    #             return 1
    #     class Foo2(FooBase):
    #         @FooBase.spam.getter
    #         def spam(self):
    #             """a new docstring"""
    #             return 2
    #     self.assertEqual(Foo.spam.__doc__, "a new docstring")

class TestDecorators(unittest.TestCase):

    def test_single(self):
        class C(object):
            @staticmethod
            def foo(): return 42
        self.assertEqual(C.foo(), 42)
        self.assertEqual(C().foo(), 42)

    def test_staticmethods(self):
        # Testing static methods...
        class C(object):
            def foo(*a): return a
            goo = staticmethod(foo)
        c = C()
        self.assertEqual(C.goo(1), (1,))
        self.assertEqual(c.goo(1), (1,))
        self.assertEqual(c.foo(1), (c, 1,))
        class D(C):
            pass
        d = D()
        self.assertEqual(D.goo(1), (1,))
        self.assertEqual(d.goo(1), (1,))
        self.assertEqual(d.foo(1), (d, 1))
        self.assertEqual(D.foo(d, 1), (d, 1))

    def test_classmethods(self):
        # Testing class methods...
        class C(object):
            def foo(*a): return a
            goo = classmethod(foo)
        c = C()
        self.assertEqual(C.goo(1), (C, 1))
        self.assertEqual(c.goo(1), (C, 1))
        self.assertEqual(c.foo(1), (c, 1))
        class D(C):
            pass
        d = D()
        self.assertEqual(D.goo(1), (D, 1))
        self.assertEqual(d.goo(1), (D, 1))
        self.assertEqual(d.foo(1), (d, 1))
        self.assertEqual(D.foo(d, 1), (d, 1))
        # Test for a specific crash (SF bug 528132)
        def f(cls, arg): return (cls, arg)
        ff = classmethod(f)
        self.assertEqual(ff.__get__(0, int)(42), (int, 42))
        self.assertEqual(ff.__get__(0)(42), (int, 42))


class PST(unittest.TestCase):
    def test_foo(self):
        class X:
            def __init__(self):
                self._foo = 0

            @property
            def foo(self):
                time.sleep(0.001)
                return self._foo

            @foo.setter
            def foo(self, new_value):
                time.sleep(0.001)
                self._foo = new_value + 2

        x = X()
        self.assertEqual(x.foo, 0)
        x.foo = 42
        self.assertEqual(x.foo, 44)

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