(root)/
Python-3.11.7/
Lib/
test/
test_dynamicclassattribute.py
       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 ESC[4;38;5;81mPropertyBase(ESC[4;38;5;149mException):
      10      pass
      11  
      12  class ESC[4;38;5;81mPropertyGet(ESC[4;38;5;149mPropertyBase):
      13      pass
      14  
      15  class ESC[4;38;5;81mPropertySet(ESC[4;38;5;149mPropertyBase):
      16      pass
      17  
      18  class ESC[4;38;5;81mPropertyDel(ESC[4;38;5;149mPropertyBase):
      19      pass
      20  
      21  class ESC[4;38;5;81mBaseClass(ESC[4;38;5;149mobject):
      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 ESC[4;38;5;81mSubClass(ESC[4;38;5;149mBaseClass):
      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 ESC[4;38;5;81mPropertyDocBase(ESC[4;38;5;149mobject):
      56      _spam = 1
      57      def _get_spam(self):
      58          return self._spam
      59      spam = DynamicClassAttribute(_get_spam, doc="spam spam spam")
      60  
      61  class ESC[4;38;5;81mPropertyDocSub(ESC[4;38;5;149mPropertyDocBase):
      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 ESC[4;38;5;81mPropertySubNewGetter(ESC[4;38;5;149mBaseClass):
      69      spam = BaseClass.__dict__['spam']
      70      @spam.getter
      71      def spam(self):
      72          """new docstring"""
      73          return 5
      74  
      75  class ESC[4;38;5;81mPropertyNewGetter(ESC[4;38;5;149mobject):
      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 ESC[4;38;5;81mClassWithAbstractVirtualProperty(metaclass=ESC[4;38;5;149mabcESC[4;38;5;149m.ESC[4;38;5;149mABCMeta):
      86      @DynamicClassAttribute
      87      @abc.abstractmethod
      88      def color():
      89          pass
      90  
      91  class ESC[4;38;5;81mClassWithPropertyAbstractVirtual(metaclass=ESC[4;38;5;149mabcESC[4;38;5;149m.ESC[4;38;5;149mABCMeta):
      92      @abc.abstractmethod
      93      @DynamicClassAttribute
      94      def color():
      95          pass
      96  
      97  class ESC[4;38;5;81mPropertyTests(ESC[4;38;5;149munittestESC[4;38;5;149m.ESC[4;38;5;149mTestCase):
      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 ESC[4;38;5;81mC(ESC[4;38;5;149mobject):
     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 ESC[4;38;5;81mNotBool(ESC[4;38;5;149mobject):
     160              def __bool__(self):
     161                  raise ValueError()
     162              __len__ = __bool__
     163          with self.assertRaises(ValueError):
     164              class ESC[4;38;5;81mC(ESC[4;38;5;149mobject):
     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 ESC[4;38;5;81mAPV(ESC[4;38;5;149mClassWithPropertyAbstractVirtual):
     174              pass
     175          self.assertRaises(TypeError, APV)
     176          class ESC[4;38;5;81mAVP(ESC[4;38;5;149mClassWithAbstractVirtualProperty):
     177              pass
     178          self.assertRaises(TypeError, AVP)
     179          class ESC[4;38;5;81mOkay1(ESC[4;38;5;149mClassWithAbstractVirtualProperty):
     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 ESC[4;38;5;81mOkay2(ESC[4;38;5;149mClassWithAbstractVirtualProperty):
     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 ESC[4;38;5;81mPropertySub(ESC[4;38;5;149mDynamicClassAttribute):
     201      """This is a subclass of DynamicClassAttribute"""
     202  
     203  class ESC[4;38;5;81mPropertySubSlots(ESC[4;38;5;149mDynamicClassAttribute):
     204      """This is a subclass of DynamicClassAttribute that defines __slots__"""
     205      __slots__ = ()
     206  
     207  class ESC[4;38;5;81mPropertySubclassTests(ESC[4;38;5;149munittestESC[4;38;5;149m.ESC[4;38;5;149mTestCase):
     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 ESC[4;38;5;81mFoo(ESC[4;38;5;149mobject):
     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 ESC[4;38;5;81mFoo(ESC[4;38;5;149mobject):
     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 ESC[4;38;5;81mFoo(ESC[4;38;5;149mobject):
     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 ESC[4;38;5;81mFooSub(ESC[4;38;5;149mFoo):
     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 ESC[4;38;5;81mFoo(ESC[4;38;5;149mobject):
     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 ESC[4;38;5;81mFooBase(ESC[4;38;5;149mobject):
     285              @PropertySub
     286              def spam(self):
     287                  """a docstring"""
     288                  return 1
     289          class ESC[4;38;5;81mFoo2(ESC[4;38;5;149mFooBase):
     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()