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 @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 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 218# 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 228 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 240 @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 252 @unittest.skipIf(sys.flags.optimize >= 2, 253 "Docstrings are omitted with -O2 and above") 254 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 285 @unittest.skipIf(sys.flags.optimize >= 2, 286 "Docstrings are omitted with -O2 and above") 287 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 312class _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 352 353if __name__ == '__main__': 354 unittest.main() 355