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