1# Test case for property
2# more tests are in test_descr
3
4import sys
5import unittest
6from test import support
7
8class PropertyBase(Exception):
9    pass
10
11class PropertyGet(PropertyBase):
12    pass
13
14class PropertySet(PropertyBase):
15    pass
16
17class PropertyDel(PropertyBase):
18    pass
19
20class BaseClass(object):
21    def __init__(self):
22        self._spam = 5
23
24    @property
25    def spam(self):
26        """BaseClass.getter"""
27        return self._spam
28
29    @spam.setter
30    def spam(self, value):
31        self._spam = value
32
33    @spam.deleter
34    def spam(self):
35        del self._spam
36
37class SubClass(BaseClass):
38
39    @BaseClass.spam.getter
40    def spam(self):
41        """SubClass.getter"""
42        raise PropertyGet(self._spam)
43
44    @spam.setter
45    def spam(self, value):
46        raise PropertySet(self._spam)
47
48    @spam.deleter
49    def spam(self):
50        raise PropertyDel(self._spam)
51
52class PropertyDocBase(object):
53    _spam = 1
54    def _get_spam(self):
55        return self._spam
56    spam = property(_get_spam, doc="spam spam spam")
57
58class PropertyDocSub(PropertyDocBase):
59    @PropertyDocBase.spam.getter
60    def spam(self):
61        """The decorator does not use this doc string"""
62        return self._spam
63
64class PropertySubNewGetter(BaseClass):
65    @BaseClass.spam.getter
66    def spam(self):
67        """new docstring"""
68        return 5
69
70class PropertyNewGetter(object):
71    @property
72    def spam(self):
73        """original docstring"""
74        return 1
75    @spam.getter
76    def spam(self):
77        """new docstring"""
78        return 8
79
80class PropertyTests(unittest.TestCase):
81    def test_property_decorator_baseclass(self):
82        # see #1620
83        base = BaseClass()
84        self.assertEqual(base.spam, 5)
85        self.assertEqual(base._spam, 5)
86        base.spam = 10
87        self.assertEqual(base.spam, 10)
88        self.assertEqual(base._spam, 10)
89        delattr(base, "spam")
90        self.assertTrue(not hasattr(base, "spam"))
91        self.assertTrue(not hasattr(base, "_spam"))
92        base.spam = 20
93        self.assertEqual(base.spam, 20)
94        self.assertEqual(base._spam, 20)
95
96    def test_property_decorator_subclass(self):
97        # see #1620
98        sub = SubClass()
99        self.assertRaises(PropertyGet, getattr, sub, "spam")
100        self.assertRaises(PropertySet, setattr, sub, "spam", None)
101        self.assertRaises(PropertyDel, delattr, sub, "spam")
102
103    @unittest.skipIf(sys.flags.optimize >= 2,
104                     "Docstrings are omitted with -O2 and above")
105    def test_property_decorator_subclass_doc(self):
106        sub = SubClass()
107        self.assertEqual(sub.__class__.spam.__doc__, "SubClass.getter")
108
109    @unittest.skipIf(sys.flags.optimize >= 2,
110                     "Docstrings are omitted with -O2 and above")
111    def test_property_decorator_baseclass_doc(self):
112        base = BaseClass()
113        self.assertEqual(base.__class__.spam.__doc__, "BaseClass.getter")
114
115    def test_property_decorator_doc(self):
116        base = PropertyDocBase()
117        sub = PropertyDocSub()
118        self.assertEqual(base.__class__.spam.__doc__, "spam spam spam")
119        self.assertEqual(sub.__class__.spam.__doc__, "spam spam spam")
120
121    @unittest.skipIf(sys.flags.optimize >= 2,
122                     "Docstrings are omitted with -O2 and above")
123    def test_property_getter_doc_override(self):
124        newgettersub = PropertySubNewGetter()
125        self.assertEqual(newgettersub.spam, 5)
126        self.assertEqual(newgettersub.__class__.spam.__doc__, "new docstring")
127        newgetter = PropertyNewGetter()
128        self.assertEqual(newgetter.spam, 8)
129        self.assertEqual(newgetter.__class__.spam.__doc__, "new docstring")
130
131    def test_property___isabstractmethod__descriptor(self):
132        for val in (True, False, [], [1], '', '1'):
133            class C(object):
134                def foo(self):
135                    pass
136                foo.__isabstractmethod__ = val
137                foo = property(foo)
138            self.assertIs(C.foo.__isabstractmethod__, bool(val))
139
140        # check that the property's __isabstractmethod__ descriptor does the
141        # right thing when presented with a value that fails truth testing:
142        class NotBool(object):
143            def __bool__(self):
144                raise ValueError()
145            __len__ = __bool__
146        with self.assertRaises(ValueError):
147            class C(object):
148                def foo(self):
149                    pass
150                foo.__isabstractmethod__ = NotBool()
151                foo = property(foo)
152            C.foo.__isabstractmethod__
153
154    @unittest.skipIf(sys.flags.optimize >= 2,
155                     "Docstrings are omitted with -O2 and above")
156    def test_property_builtin_doc_writable(self):
157        p = property(doc='basic')
158        self.assertEqual(p.__doc__, 'basic')
159        p.__doc__ = 'extended'
160        self.assertEqual(p.__doc__, 'extended')
161
162    @unittest.skipIf(sys.flags.optimize >= 2,
163                     "Docstrings are omitted with -O2 and above")
164    def test_property_decorator_doc_writable(self):
165        class PropertyWritableDoc(object):
166
167            @property
168            def spam(self):
169                """Eggs"""
170                return "eggs"
171
172        sub = PropertyWritableDoc()
173        self.assertEqual(sub.__class__.spam.__doc__, 'Eggs')
174        sub.__class__.spam.__doc__ = 'Spam'
175        self.assertEqual(sub.__class__.spam.__doc__, 'Spam')
176
177    @support.refcount_test
178    def test_refleaks_in___init__(self):
179        gettotalrefcount = support.get_attribute(sys, 'gettotalrefcount')
180        fake_prop = property('fget', 'fset', 'fdel', 'doc')
181        refs_before = gettotalrefcount()
182        for i in range(100):
183            fake_prop.__init__('fget', 'fset', 'fdel', 'doc')
184        self.assertAlmostEqual(gettotalrefcount() - refs_before, 0, delta=10)
185
186
187# Issue 5890: subclasses of property do not preserve method __doc__ strings
188class PropertySub(property):
189    """This is a subclass of property"""
190
191class PropertySubSlots(property):
192    """This is a subclass of property that defines __slots__"""
193    __slots__ = ()
194
195class PropertySubclassTests(unittest.TestCase):
196
197    def test_slots_docstring_copy_exception(self):
198        try:
199            class Foo(object):
200                @PropertySubSlots
201                def spam(self):
202                    """Trying to copy this docstring will raise an exception"""
203                    return 1
204        except AttributeError:
205            pass
206        else:
207            raise Exception("AttributeError not raised")
208
209    @unittest.skipIf(sys.flags.optimize >= 2,
210                     "Docstrings are omitted with -O2 and above")
211    def test_docstring_copy(self):
212        class Foo(object):
213            @PropertySub
214            def spam(self):
215                """spam wrapped in property subclass"""
216                return 1
217        self.assertEqual(
218            Foo.spam.__doc__,
219            "spam wrapped in property subclass")
220
221    @unittest.skipIf(sys.flags.optimize >= 2,
222                     "Docstrings are omitted with -O2 and above")
223    def test_property_setter_copies_getter_docstring(self):
224        class Foo(object):
225            def __init__(self): self._spam = 1
226            @PropertySub
227            def spam(self):
228                """spam wrapped in property subclass"""
229                return self._spam
230            @spam.setter
231            def spam(self, value):
232                """this docstring is ignored"""
233                self._spam = value
234        foo = Foo()
235        self.assertEqual(foo.spam, 1)
236        foo.spam = 2
237        self.assertEqual(foo.spam, 2)
238        self.assertEqual(
239            Foo.spam.__doc__,
240            "spam wrapped in property subclass")
241        class FooSub(Foo):
242            @Foo.spam.setter
243            def spam(self, value):
244                """another ignored docstring"""
245                self._spam = 'eggs'
246        foosub = FooSub()
247        self.assertEqual(foosub.spam, 1)
248        foosub.spam = 7
249        self.assertEqual(foosub.spam, 'eggs')
250        self.assertEqual(
251            FooSub.spam.__doc__,
252            "spam wrapped in property subclass")
253
254    @unittest.skipIf(sys.flags.optimize >= 2,
255                     "Docstrings are omitted with -O2 and above")
256    def test_property_new_getter_new_docstring(self):
257
258        class Foo(object):
259            @PropertySub
260            def spam(self):
261                """a docstring"""
262                return 1
263            @spam.getter
264            def spam(self):
265                """a new docstring"""
266                return 2
267        self.assertEqual(Foo.spam.__doc__, "a new docstring")
268        class FooBase(object):
269            @PropertySub
270            def spam(self):
271                """a docstring"""
272                return 1
273        class Foo2(FooBase):
274            @FooBase.spam.getter
275            def spam(self):
276                """a new docstring"""
277                return 2
278        self.assertEqual(Foo.spam.__doc__, "a new docstring")
279
280
281
282if __name__ == '__main__':
283    unittest.main()
284