cpython: 3752c94368dd (original) (raw)
new file mode 100644 --- /dev/null +++ b/Lib/test/test_dynamicclassattribute.py @@ -0,0 +1,304 @@ +# Test case for DynamicClassAttribute +# more tests are in test_descr + +import abc +import sys +import unittest +from test.support import run_unittest +from types import DynamicClassAttribute + +class PropertyBase(Exception):
+ +class PropertyGet(PropertyBase):
+ +class PropertySet(PropertyBase):
+ +class PropertyDel(PropertyBase):
+ +class SubClass(BaseClass): +
- @spam.getter
- def spam(self):
"""SubClass.getter"""[](#l1.49)
raise PropertyGet(self._spam)[](#l1.50)
+ +class PropertyDocBase(object):
- _spam = 1
- def _get_spam(self):
return self._spam[](#l1.63)
- spam = DynamicClassAttribute(_get_spam, doc="spam spam spam")
+ +class PropertyDocSub(PropertyDocBase):
- spam = PropertyDocBase.dict['spam']
- @spam.getter
- def spam(self):
"""The decorator does not use this doc string"""[](#l1.70)
return self._spam[](#l1.71)
+ +class PropertySubNewGetter(BaseClass):
- spam = BaseClass.dict['spam']
- @spam.getter
- def spam(self):
"""new docstring"""[](#l1.77)
return 5[](#l1.78)
+ +class PropertyNewGetter(object):
- @DynamicClassAttribute
- def spam(self):
"""original docstring"""[](#l1.83)
return 1[](#l1.84)
- @spam.getter
- def spam(self):
"""new docstring"""[](#l1.87)
return 8[](#l1.88)
+ +class ClassWithAbstractVirtualProperty(metaclass=abc.ABCMeta):
+ +class ClassWithPropertyAbstractVirtual(metaclass=abc.ABCMeta):
+ +class PropertyTests(unittest.TestCase):
- def test_property_decorator_baseclass(self):
# see #1620[](#l1.104)
base = BaseClass()[](#l1.105)
self.assertEqual(base.spam, 5)[](#l1.106)
self.assertEqual(base._spam, 5)[](#l1.107)
base.spam = 10[](#l1.108)
self.assertEqual(base.spam, 10)[](#l1.109)
self.assertEqual(base._spam, 10)[](#l1.110)
delattr(base, "spam")[](#l1.111)
self.assertTrue(not hasattr(base, "spam"))[](#l1.112)
self.assertTrue(not hasattr(base, "_spam"))[](#l1.113)
base.spam = 20[](#l1.114)
self.assertEqual(base.spam, 20)[](#l1.115)
self.assertEqual(base._spam, 20)[](#l1.116)
- def test_property_decorator_subclass(self):
# see #1620[](#l1.119)
sub = SubClass()[](#l1.120)
self.assertRaises(PropertyGet, getattr, sub, "spam")[](#l1.121)
self.assertRaises(PropertySet, setattr, sub, "spam", None)[](#l1.122)
self.assertRaises(PropertyDel, delattr, sub, "spam")[](#l1.123)
- @unittest.skipIf(sys.flags.optimize >= 2,
"Docstrings are omitted with -O2 and above")[](#l1.126)
- def test_property_decorator_subclass_doc(self):
sub = SubClass()[](#l1.128)
self.assertEqual(sub.__class__.__dict__['spam'].__doc__, "SubClass.getter")[](#l1.129)
- @unittest.skipIf(sys.flags.optimize >= 2,
"Docstrings are omitted with -O2 and above")[](#l1.132)
- def test_property_decorator_baseclass_doc(self):
base = BaseClass()[](#l1.134)
self.assertEqual(base.__class__.__dict__['spam'].__doc__, "BaseClass.getter")[](#l1.135)
- def test_property_decorator_doc(self):
base = PropertyDocBase()[](#l1.138)
sub = PropertyDocSub()[](#l1.139)
self.assertEqual(base.__class__.__dict__['spam'].__doc__, "spam spam spam")[](#l1.140)
self.assertEqual(sub.__class__.__dict__['spam'].__doc__, "spam spam spam")[](#l1.141)
- @unittest.skipIf(sys.flags.optimize >= 2,
"Docstrings are omitted with -O2 and above")[](#l1.144)
- def test_property_getter_doc_override(self):
newgettersub = PropertySubNewGetter()[](#l1.146)
self.assertEqual(newgettersub.spam, 5)[](#l1.147)
self.assertEqual(newgettersub.__class__.__dict__['spam'].__doc__, "new docstring")[](#l1.148)
newgetter = PropertyNewGetter()[](#l1.149)
self.assertEqual(newgetter.spam, 8)[](#l1.150)
self.assertEqual(newgetter.__class__.__dict__['spam'].__doc__, "new docstring")[](#l1.151)
- def test_property___isabstractmethod__descriptor(self):
for val in (True, False, [], [1], '', '1'):[](#l1.154)
class C(object):[](#l1.155)
def foo(self):[](#l1.156)
pass[](#l1.157)
foo.__isabstractmethod__ = val[](#l1.158)
foo = DynamicClassAttribute(foo)[](#l1.159)
self.assertIs(C.__dict__['foo'].__isabstractmethod__, bool(val))[](#l1.160)
# check that the DynamicClassAttribute's __isabstractmethod__ descriptor does the[](#l1.162)
# right thing when presented with a value that fails truth testing:[](#l1.163)
class NotBool(object):[](#l1.164)
def __nonzero__(self):[](#l1.165)
raise ValueError()[](#l1.166)
__len__ = __nonzero__[](#l1.167)
with self.assertRaises(ValueError):[](#l1.168)
class C(object):[](#l1.169)
def foo(self):[](#l1.170)
pass[](#l1.171)
foo.__isabstractmethod__ = NotBool()[](#l1.172)
foo = DynamicClassAttribute(foo)[](#l1.173)
- def test_abstract_virtual(self):
self.assertRaises(TypeError, ClassWithAbstractVirtualProperty)[](#l1.176)
self.assertRaises(TypeError, ClassWithPropertyAbstractVirtual)[](#l1.177)
class APV(ClassWithPropertyAbstractVirtual):[](#l1.178)
pass[](#l1.179)
self.assertRaises(TypeError, APV)[](#l1.180)
class AVP(ClassWithAbstractVirtualProperty):[](#l1.181)
pass[](#l1.182)
self.assertRaises(TypeError, AVP)[](#l1.183)
class Okay1(ClassWithAbstractVirtualProperty):[](#l1.184)
@DynamicClassAttribute[](#l1.185)
def color(self):[](#l1.186)
return self._color[](#l1.187)
def __init__(self):[](#l1.188)
self._color = 'cyan'[](#l1.189)
with self.assertRaises(AttributeError):[](#l1.190)
Okay1.color[](#l1.191)
self.assertEqual(Okay1().color, 'cyan')[](#l1.192)
class Okay2(ClassWithAbstractVirtualProperty):[](#l1.193)
@DynamicClassAttribute[](#l1.194)
def color(self):[](#l1.195)
return self._color[](#l1.196)
def __init__(self):[](#l1.197)
self._color = 'magenta'[](#l1.198)
with self.assertRaises(AttributeError):[](#l1.199)
Okay2.color[](#l1.200)
self.assertEqual(Okay2().color, 'magenta')[](#l1.201)
+ + +# Issue 5890: subclasses of DynamicClassAttribute do not preserve method doc strings +class PropertySub(DynamicClassAttribute):
+ +class PropertySubSlots(DynamicClassAttribute):
+ +class PropertySubclassTests(unittest.TestCase): +
- @unittest.skipIf(hasattr(PropertySubSlots, 'doc'),
"__doc__ is already present, __slots__ will have no effect")[](#l1.215)
- def test_slots_docstring_copy_exception(self):
try:[](#l1.217)
class Foo(object):[](#l1.218)
@PropertySubSlots[](#l1.219)
def spam(self):[](#l1.220)
"""Trying to copy this docstring will raise an exception"""[](#l1.221)
return 1[](#l1.222)
print('\n',spam.__doc__)[](#l1.223)
except AttributeError:[](#l1.224)
pass[](#l1.225)
else:[](#l1.226)
raise Exception("AttributeError not raised")[](#l1.227)
- @unittest.skipIf(sys.flags.optimize >= 2,
"Docstrings are omitted with -O2 and above")[](#l1.230)
- def test_docstring_copy(self):
class Foo(object):[](#l1.232)
@PropertySub[](#l1.233)
def spam(self):[](#l1.234)
"""spam wrapped in DynamicClassAttribute subclass"""[](#l1.235)
return 1[](#l1.236)
self.assertEqual([](#l1.237)
Foo.__dict__['spam'].__doc__,[](#l1.238)
"spam wrapped in DynamicClassAttribute subclass")[](#l1.239)
- @unittest.skipIf(sys.flags.optimize >= 2,
"Docstrings are omitted with -O2 and above")[](#l1.242)
- def test_property_setter_copies_getter_docstring(self):
class Foo(object):[](#l1.244)
def __init__(self): self._spam = 1[](#l1.245)
@PropertySub[](#l1.246)
def spam(self):[](#l1.247)
"""spam wrapped in DynamicClassAttribute subclass"""[](#l1.248)
return self._spam[](#l1.249)
@spam.setter[](#l1.250)
def spam(self, value):[](#l1.251)
"""this docstring is ignored"""[](#l1.252)
self._spam = value[](#l1.253)
foo = Foo()[](#l1.254)
self.assertEqual(foo.spam, 1)[](#l1.255)
foo.spam = 2[](#l1.256)
self.assertEqual(foo.spam, 2)[](#l1.257)
self.assertEqual([](#l1.258)
Foo.__dict__['spam'].__doc__,[](#l1.259)
"spam wrapped in DynamicClassAttribute subclass")[](#l1.260)
class FooSub(Foo):[](#l1.261)
spam = Foo.__dict__['spam'][](#l1.262)
@spam.setter[](#l1.263)
def spam(self, value):[](#l1.264)
"""another ignored docstring"""[](#l1.265)
self._spam = 'eggs'[](#l1.266)
foosub = FooSub()[](#l1.267)
self.assertEqual(foosub.spam, 1)[](#l1.268)
foosub.spam = 7[](#l1.269)
self.assertEqual(foosub.spam, 'eggs')[](#l1.270)
self.assertEqual([](#l1.271)
FooSub.__dict__['spam'].__doc__,[](#l1.272)
"spam wrapped in DynamicClassAttribute subclass")[](#l1.273)
- @unittest.skipIf(sys.flags.optimize >= 2,
"Docstrings are omitted with -O2 and above")[](#l1.276)
- def test_property_new_getter_new_docstring(self):
class Foo(object):[](#l1.279)
@PropertySub[](#l1.280)
def spam(self):[](#l1.281)
"""a docstring"""[](#l1.282)
return 1[](#l1.283)
@spam.getter[](#l1.284)
def spam(self):[](#l1.285)
"""a new docstring"""[](#l1.286)
return 2[](#l1.287)
self.assertEqual(Foo.__dict__['spam'].__doc__, "a new docstring")[](#l1.288)
class FooBase(object):[](#l1.289)
@PropertySub[](#l1.290)
def spam(self):[](#l1.291)
"""a docstring"""[](#l1.292)
return 1[](#l1.293)
class Foo2(FooBase):[](#l1.294)
spam = FooBase.__dict__['spam'][](#l1.295)
@spam.getter[](#l1.296)
def spam(self):[](#l1.297)
"""a new docstring"""[](#l1.298)
return 2[](#l1.299)
self.assertEqual(Foo.__dict__['spam'].__doc__, "a new docstring")[](#l1.300)