blob: 7f3813fc8cd15eadfffcd1120976fe402e599c1f [file] [log] [blame]
Christian Heimes0449f632007-12-15 01:27:15 +00001# Test case for property
2# more tests are in test_descr
3
R. David Murray378c0cf2010-02-24 01:46:21 +00004import sys
Christian Heimes0449f632007-12-15 01:27:15 +00005import unittest
Oren Milmand019bc82018-02-13 12:28:33 +02006from test import support
Christian Heimes0449f632007-12-15 01:27:15 +00007
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
R. David Murrayb18500d2009-05-04 22:59:07 +000064class 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
Christian Heimes0449f632007-12-15 01:27:15 +000080class 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")
Benjamin Petersonc9c0f202009-06-30 23:06:06 +000090 self.assertTrue(not hasattr(base, "spam"))
91 self.assertTrue(not hasattr(base, "_spam"))
Christian Heimes0449f632007-12-15 01:27:15 +000092 base.spam = 20
93 self.assertEqual(base.spam, 20)
94 self.assertEqual(base._spam, 20)
Christian Heimes0449f632007-12-15 01:27:15 +000095
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")
R. David Murray378c0cf2010-02-24 01:46:21 +0000102
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()
Christian Heimes0449f632007-12-15 01:27:15 +0000107 self.assertEqual(sub.__class__.spam.__doc__, "SubClass.getter")
108
R. David Murray378c0cf2010-02-24 01:46:21 +0000109 @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
Christian Heimes0449f632007-12-15 01:27:15 +0000115 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
Benjamin Peterson902d2bd2010-06-28 15:43:25 +0000121 @unittest.skipIf(sys.flags.optimize >= 2,
R. David Murray378c0cf2010-02-24 01:46:21 +0000122 "Docstrings are omitted with -O2 and above")
R. David Murrayb18500d2009-05-04 22:59:07 +0000123 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
Benjamin Petersonbfebb7b2011-12-15 15:34:02 -0500131 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):
Serhiy Storchakaa60c2fe2015-03-12 21:56:08 +0200143 def __bool__(self):
Benjamin Petersonbfebb7b2011-12-15 15:34:02 -0500144 raise ValueError()
Serhiy Storchakaa60c2fe2015-03-12 21:56:08 +0200145 __len__ = __bool__
Benjamin Petersonbfebb7b2011-12-15 15:34:02 -0500146 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
Raymond Hettingereac503a2015-05-13 01:09:59 -0700154 @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):
Berker Peksaga7d81272015-12-11 23:48:13 +0200165 class PropertyWritableDoc(object):
166
167 @property
168 def spam(self):
169 """Eggs"""
170 return "eggs"
171
Raymond Hettingereac503a2015-05-13 01:09:59 -0700172 sub = PropertyWritableDoc()
173 self.assertEqual(sub.__class__.spam.__doc__, 'Eggs')
174 sub.__class__.spam.__doc__ = 'Spam'
175 self.assertEqual(sub.__class__.spam.__doc__, 'Spam')
R. David Murrayb18500d2009-05-04 22:59:07 +0000176
Oren Milmand019bc82018-02-13 12:28:33 +0200177 @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
Berker Peksag805f8f92019-08-25 01:37:25 +0300186 @unittest.skipIf(sys.flags.optimize >= 2,
187 "Docstrings are omitted with -O2 and above")
188 def test_class_property(self):
189 class A:
190 @classmethod
191 @property
192 def __doc__(cls):
193 return 'A doc for %r' % cls.__name__
194 self.assertEqual(A.__doc__, "A doc for 'A'")
195
196 @unittest.skipIf(sys.flags.optimize >= 2,
197 "Docstrings are omitted with -O2 and above")
198 def test_class_property_override(self):
199 class A:
200 """First"""
201 @classmethod
202 @property
203 def __doc__(cls):
204 return 'Second'
205 self.assertEqual(A.__doc__, 'Second')
206
Yurii Karabasc56387f2020-12-30 11:51:24 +0200207 def test_property_set_name_incorrect_args(self):
208 p = property()
209
210 for i in (0, 1, 3):
211 with self.assertRaisesRegex(
212 TypeError,
213 fr'^__set_name__\(\) takes 2 positional arguments but {i} were given$'
214 ):
215 p.__set_name__(*([0] * i))
216
Oren Milmand019bc82018-02-13 12:28:33 +0200217
R. David Murrayb18500d2009-05-04 22:59:07 +0000218# Issue 5890: subclasses of property do not preserve method __doc__ strings
219class PropertySub(property):
220 """This is a subclass of property"""
221
222class PropertySubSlots(property):
223 """This is a subclass of property that defines __slots__"""
224 __slots__ = ()
225
226class PropertySubclassTests(unittest.TestCase):
227
R. David Murrayb18500d2009-05-04 22:59:07 +0000228 def test_slots_docstring_copy_exception(self):
229 try:
230 class Foo(object):
231 @PropertySubSlots
232 def spam(self):
233 """Trying to copy this docstring will raise an exception"""
234 return 1
235 except AttributeError:
236 pass
237 else:
238 raise Exception("AttributeError not raised")
239
R. David Murray378c0cf2010-02-24 01:46:21 +0000240 @unittest.skipIf(sys.flags.optimize >= 2,
241 "Docstrings are omitted with -O2 and above")
242 def test_docstring_copy(self):
243 class Foo(object):
244 @PropertySub
245 def spam(self):
246 """spam wrapped in property subclass"""
247 return 1
248 self.assertEqual(
249 Foo.spam.__doc__,
250 "spam wrapped in property subclass")
251
Serhiy Storchaka8e0ae2a2013-01-28 13:25:44 +0200252 @unittest.skipIf(sys.flags.optimize >= 2,
R. David Murray378c0cf2010-02-24 01:46:21 +0000253 "Docstrings are omitted with -O2 and above")
R. David Murrayb18500d2009-05-04 22:59:07 +0000254 def test_property_setter_copies_getter_docstring(self):
255 class Foo(object):
256 def __init__(self): self._spam = 1
257 @PropertySub
258 def spam(self):
259 """spam wrapped in property subclass"""
260 return self._spam
261 @spam.setter
262 def spam(self, value):
263 """this docstring is ignored"""
264 self._spam = value
265 foo = Foo()
266 self.assertEqual(foo.spam, 1)
267 foo.spam = 2
268 self.assertEqual(foo.spam, 2)
269 self.assertEqual(
270 Foo.spam.__doc__,
271 "spam wrapped in property subclass")
272 class FooSub(Foo):
273 @Foo.spam.setter
274 def spam(self, value):
275 """another ignored docstring"""
276 self._spam = 'eggs'
277 foosub = FooSub()
278 self.assertEqual(foosub.spam, 1)
279 foosub.spam = 7
280 self.assertEqual(foosub.spam, 'eggs')
281 self.assertEqual(
282 FooSub.spam.__doc__,
283 "spam wrapped in property subclass")
284
Serhiy Storchaka8e0ae2a2013-01-28 13:25:44 +0200285 @unittest.skipIf(sys.flags.optimize >= 2,
R. David Murray378c0cf2010-02-24 01:46:21 +0000286 "Docstrings are omitted with -O2 and above")
R. David Murrayb18500d2009-05-04 22:59:07 +0000287 def test_property_new_getter_new_docstring(self):
288
289 class Foo(object):
290 @PropertySub
291 def spam(self):
292 """a docstring"""
293 return 1
294 @spam.getter
295 def spam(self):
296 """a new docstring"""
297 return 2
298 self.assertEqual(Foo.spam.__doc__, "a new docstring")
299 class FooBase(object):
300 @PropertySub
301 def spam(self):
302 """a docstring"""
303 return 1
304 class Foo2(FooBase):
305 @FooBase.spam.getter
306 def spam(self):
307 """a new docstring"""
308 return 2
309 self.assertEqual(Foo.spam.__doc__, "a new docstring")
310
311
Yurii Karabasc56387f2020-12-30 11:51:24 +0200312class _PropertyUnreachableAttribute:
313 msg_format = None
314 obj = None
315 cls = None
316
317 def _format_exc_msg(self, msg):
318 return self.msg_format.format(msg)
319
320 @classmethod
321 def setUpClass(cls):
322 cls.obj = cls.cls()
323
324 def test_get_property(self):
325 with self.assertRaisesRegex(AttributeError, self._format_exc_msg("unreadable attribute")):
326 self.obj.foo
327
328 def test_set_property(self):
329 with self.assertRaisesRegex(AttributeError, self._format_exc_msg("can't set attribute")):
330 self.obj.foo = None
331
332 def test_del_property(self):
333 with self.assertRaisesRegex(AttributeError, self._format_exc_msg("can't delete attribute")):
334 del self.obj.foo
335
336
337class PropertyUnreachableAttributeWithName(_PropertyUnreachableAttribute, unittest.TestCase):
338 msg_format = "^{} 'foo'$"
339
340 class cls:
341 foo = property()
342
343
344class PropertyUnreachableAttributeNoName(_PropertyUnreachableAttribute, unittest.TestCase):
345 msg_format = "^{}$"
346
347 class cls:
348 pass
349
350 cls.foo = property()
351
R. David Murrayb18500d2009-05-04 22:59:07 +0000352
Christian Heimes0449f632007-12-15 01:27:15 +0000353if __name__ == '__main__':
Zachary Ware38c707e2015-04-13 15:00:43 -0500354 unittest.main()