1 # Test case for property 2 # more tests are in test_descr 3 4 import sys 5 import unittest 6 from test.test_support import run_unittest 7 8 class PropertyBase(Exception): 9 pass 10 11 class PropertyGet(PropertyBase): 12 pass 13 14 class PropertySet(PropertyBase): 15 pass 16 17 class PropertyDel(PropertyBase): 18 pass 19 20 class 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 37 class 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 52 class PropertyDocBase(object): 53 _spam = 1 54 def _get_spam(self): 55 return self._spam 56 spam = property(_get_spam, doc="spam spam spam") 57 58 class PropertyDocSub(PropertyDocBase): 59 @PropertyDocBase.spam.getter 60 def spam(self): 61 """The decorator does not use this doc string""" 62 return self._spam 63 64 class PropertySubNewGetter(BaseClass): 65 @BaseClass.spam.getter 66 def spam(self): 67 """new docstring""" 68 return 5 69 70 class 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 80 class 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 132 # Issue 5890: subclasses of property do not preserve method __doc__ strings 133 class PropertySub(property): 134 """This is a subclass of property""" 135 136 class PropertySubSlots(property): 137 """This is a subclass of property that defines __slots__""" 138 __slots__ = () 139 140 class PropertySubclassTests(unittest.TestCase): 141 142 def test_slots_docstring_copy_exception(self): 143 try: 144 class Foo(object): 145 @PropertySubSlots 146 def spam(self): 147 """Trying to copy this docstring will raise an exception""" 148 return 1 149 except AttributeError: 150 pass 151 else: 152 raise Exception("AttributeError not raised") 153 154 @unittest.skipIf(sys.flags.optimize >= 2, 155 "Docstrings are omitted with -O2 and above") 156 def test_docstring_copy(self): 157 class Foo(object): 158 @PropertySub 159 def spam(self): 160 """spam wrapped in property subclass""" 161 return 1 162 self.assertEqual( 163 Foo.spam.__doc__, 164 "spam wrapped in property subclass") 165 166 @unittest.skipIf(sys.flags.optimize <= 2, 167 "Docstrings are omitted with -O2 and above") 168 def test_property_setter_copies_getter_docstring(self): 169 class Foo(object): 170 def __init__(self): self._spam = 1 171 @PropertySub 172 def spam(self): 173 """spam wrapped in property subclass""" 174 return self._spam 175 @spam.setter 176 def spam(self, value): 177 """this docstring is ignored""" 178 self._spam = value 179 foo = Foo() 180 self.assertEqual(foo.spam, 1) 181 foo.spam = 2 182 self.assertEqual(foo.spam, 2) 183 self.assertEqual( 184 Foo.spam.__doc__, 185 "spam wrapped in property subclass") 186 class FooSub(Foo): 187 @Foo.spam.setter 188 def spam(self, value): 189 """another ignored docstring""" 190 self._spam = 'eggs' 191 foosub = FooSub() 192 self.assertEqual(foosub.spam, 1) 193 foosub.spam = 7 194 self.assertEqual(foosub.spam, 'eggs') 195 self.assertEqual( 196 FooSub.spam.__doc__, 197 "spam wrapped in property subclass") 198 199 @unittest.skipIf(sys.flags.optimize <= 2, 200 "Docstrings are omitted with -O2 and above") 201 def test_property_new_getter_new_docstring(self): 202 203 class Foo(object): 204 @PropertySub 205 def spam(self): 206 """a docstring""" 207 return 1 208 @spam.getter 209 def spam(self): 210 """a new docstring""" 211 return 2 212 self.assertEqual(Foo.spam.__doc__, "a new docstring") 213 class FooBase(object): 214 @PropertySub 215 def spam(self): 216 """a docstring""" 217 return 1 218 class Foo2(FooBase): 219 @FooBase.spam.getter 220 def spam(self): 221 """a new docstring""" 222 return 2 223 self.assertEqual(Foo.spam.__doc__, "a new docstring") 224 225 226 227 def test_main(): 228 run_unittest(PropertyTests, PropertySubclassTests) 229 230 if __name__ == '__main__': 231 test_main() 232