1 # Test case for property
2 # more tests are in test_descr
3
4 import sys
5 import unittest
6 from test import support
7
8 class ESC[4;38;5;81mPropertyBase(ESC[4;38;5;149mException):
9 pass
10
11 class ESC[4;38;5;81mPropertyGet(ESC[4;38;5;149mPropertyBase):
12 pass
13
14 class ESC[4;38;5;81mPropertySet(ESC[4;38;5;149mPropertyBase):
15 pass
16
17 class ESC[4;38;5;81mPropertyDel(ESC[4;38;5;149mPropertyBase):
18 pass
19
20 class ESC[4;38;5;81mBaseClass(ESC[4;38;5;149mobject):
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
37 class ESC[4;38;5;81mSubClass(ESC[4;38;5;149mBaseClass):
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
52 class ESC[4;38;5;81mPropertyDocBase(ESC[4;38;5;149mobject):
53 _spam = 1
54 def _get_spam(self):
55 return self._spam
56 spam = property(_get_spam, doc="spam spam spam")
57
58 class ESC[4;38;5;81mPropertyDocSub(ESC[4;38;5;149mPropertyDocBase):
59 @PropertyDocBase.spam.getter
60 def spam(self):
61 """The decorator does not use this doc string"""
62 return self._spam
63
64 class ESC[4;38;5;81mPropertySubNewGetter(ESC[4;38;5;149mBaseClass):
65 @BaseClass.spam.getter
66 def spam(self):
67 """new docstring"""
68 return 5
69
70 class ESC[4;38;5;81mPropertyNewGetter(ESC[4;38;5;149mobject):
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
80 class ESC[4;38;5;81mPropertyTests(ESC[4;38;5;149munittestESC[4;38;5;149m.ESC[4;38;5;149mTestCase):
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 ESC[4;38;5;81mC(ESC[4;38;5;149mobject):
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 ESC[4;38;5;81mNotBool(ESC[4;38;5;149mobject):
143 def __bool__(self):
144 raise ValueError()
145 __len__ = __bool__
146 with self.assertRaises(ValueError):
147 class ESC[4;38;5;81mC(ESC[4;38;5;149mobject):
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 ESC[4;38;5;81mPropertyWritableDoc(ESC[4;38;5;149mobject):
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 @unittest.skipIf(sys.flags.optimize >= 2,
187 "Docstrings are omitted with -O2 and above")
188 def test_class_property(self):
189 class ESC[4;38;5;81mA:
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 ESC[4;38;5;81mA:
200 """First"""
201 @classmethod
202 @property
203 def __doc__(cls):
204 return 'Second'
205 self.assertEqual(A.__doc__, 'Second')
206
207 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
217 def test_property_setname_on_property_subclass(self):
218 # https://github.com/python/cpython/issues/100942
219 # Copy was setting the name field without first
220 # verifying that the copy was an actual property
221 # instance. As a result, the code below was
222 # causing a segfault.
223
224 class ESC[4;38;5;81mpro(ESC[4;38;5;149mproperty):
225 def __new__(typ, *args, **kwargs):
226 return "abcdef"
227
228 class ESC[4;38;5;81mA:
229 pass
230
231 p = property.__new__(pro)
232 p.__set_name__(A, 1)
233 np = p.getter(lambda self: 1)
234
235 # Issue 5890: subclasses of property do not preserve method __doc__ strings
236 class ESC[4;38;5;81mPropertySub(ESC[4;38;5;149mproperty):
237 """This is a subclass of property"""
238
239 class ESC[4;38;5;81mPropertySubSlots(ESC[4;38;5;149mproperty):
240 """This is a subclass of property that defines __slots__"""
241 __slots__ = ()
242
243 class ESC[4;38;5;81mPropertySubclassTests(ESC[4;38;5;149munittestESC[4;38;5;149m.ESC[4;38;5;149mTestCase):
244
245 def test_slots_docstring_copy_exception(self):
246 try:
247 class ESC[4;38;5;81mFoo(ESC[4;38;5;149mobject):
248 @PropertySubSlots
249 def spam(self):
250 """Trying to copy this docstring will raise an exception"""
251 return 1
252 except AttributeError:
253 pass
254 else:
255 raise Exception("AttributeError not raised")
256
257 @unittest.skipIf(sys.flags.optimize >= 2,
258 "Docstrings are omitted with -O2 and above")
259 def test_docstring_copy(self):
260 class ESC[4;38;5;81mFoo(ESC[4;38;5;149mobject):
261 @PropertySub
262 def spam(self):
263 """spam wrapped in property subclass"""
264 return 1
265 self.assertEqual(
266 Foo.spam.__doc__,
267 "spam wrapped in property subclass")
268
269 @unittest.skipIf(sys.flags.optimize >= 2,
270 "Docstrings are omitted with -O2 and above")
271 def test_property_setter_copies_getter_docstring(self):
272 class ESC[4;38;5;81mFoo(ESC[4;38;5;149mobject):
273 def __init__(self): self._spam = 1
274 @PropertySub
275 def spam(self):
276 """spam wrapped in property subclass"""
277 return self._spam
278 @spam.setter
279 def spam(self, value):
280 """this docstring is ignored"""
281 self._spam = value
282 foo = Foo()
283 self.assertEqual(foo.spam, 1)
284 foo.spam = 2
285 self.assertEqual(foo.spam, 2)
286 self.assertEqual(
287 Foo.spam.__doc__,
288 "spam wrapped in property subclass")
289 class ESC[4;38;5;81mFooSub(ESC[4;38;5;149mFoo):
290 @Foo.spam.setter
291 def spam(self, value):
292 """another ignored docstring"""
293 self._spam = 'eggs'
294 foosub = FooSub()
295 self.assertEqual(foosub.spam, 1)
296 foosub.spam = 7
297 self.assertEqual(foosub.spam, 'eggs')
298 self.assertEqual(
299 FooSub.spam.__doc__,
300 "spam wrapped in property subclass")
301
302 @unittest.skipIf(sys.flags.optimize >= 2,
303 "Docstrings are omitted with -O2 and above")
304 def test_property_new_getter_new_docstring(self):
305
306 class ESC[4;38;5;81mFoo(ESC[4;38;5;149mobject):
307 @PropertySub
308 def spam(self):
309 """a docstring"""
310 return 1
311 @spam.getter
312 def spam(self):
313 """a new docstring"""
314 return 2
315 self.assertEqual(Foo.spam.__doc__, "a new docstring")
316 class ESC[4;38;5;81mFooBase(ESC[4;38;5;149mobject):
317 @PropertySub
318 def spam(self):
319 """a docstring"""
320 return 1
321 class ESC[4;38;5;81mFoo2(ESC[4;38;5;149mFooBase):
322 @FooBase.spam.getter
323 def spam(self):
324 """a new docstring"""
325 return 2
326 self.assertEqual(Foo.spam.__doc__, "a new docstring")
327
328
329 class ESC[4;38;5;81m_PropertyUnreachableAttribute:
330 msg_format = None
331 obj = None
332 cls = None
333
334 def _format_exc_msg(self, msg):
335 return self.msg_format.format(msg)
336
337 @classmethod
338 def setUpClass(cls):
339 cls.obj = cls.cls()
340
341 def test_get_property(self):
342 with self.assertRaisesRegex(AttributeError, self._format_exc_msg("has no getter")):
343 self.obj.foo
344
345 def test_set_property(self):
346 with self.assertRaisesRegex(AttributeError, self._format_exc_msg("has no setter")):
347 self.obj.foo = None
348
349 def test_del_property(self):
350 with self.assertRaisesRegex(AttributeError, self._format_exc_msg("has no deleter")):
351 del self.obj.foo
352
353
354 class ESC[4;38;5;81mPropertyUnreachableAttributeWithName(ESC[4;38;5;149m_PropertyUnreachableAttribute, ESC[4;38;5;149munittestESC[4;38;5;149m.ESC[4;38;5;149mTestCase):
355 msg_format = r"^property 'foo' of 'PropertyUnreachableAttributeWithName\.cls' object {}$"
356
357 class ESC[4;38;5;81mcls:
358 foo = property()
359
360
361 class ESC[4;38;5;81mPropertyUnreachableAttributeNoName(ESC[4;38;5;149m_PropertyUnreachableAttribute, ESC[4;38;5;149munittestESC[4;38;5;149m.ESC[4;38;5;149mTestCase):
362 msg_format = r"^property of 'PropertyUnreachableAttributeNoName\.cls' object {}$"
363
364 class ESC[4;38;5;81mcls:
365 pass
366
367 cls.foo = property()
368
369
370 if __name__ == '__main__':
371 unittest.main()