diff options
author | Ethan Furman <ethan@stoneleaf.us> | 2013-10-13 17:52:10 (GMT) |
---|---|---|
committer | Ethan Furman <ethan@stoneleaf.us> | 2013-10-13 17:52:10 (GMT) |
commit | 9efcb6bf38ccc624b7d2e13476c1264f9c5332f0 (patch) | |
tree | c38ebebff56764bcf2b3136677865ba4905596e8 | |
parent | 690a6a95bda2379dda200d6677b664b6e7004929 (diff) | |
download | cpython-9efcb6bf38ccc624b7d2e13476c1264f9c5332f0.zip cpython-9efcb6bf38ccc624b7d2e13476c1264f9c5332f0.tar.gz cpython-9efcb6bf38ccc624b7d2e13476c1264f9c5332f0.tar.bz2 |
Issue19030: commit tests for DynamicClassAttribute
-rw-r--r-- | Lib/test/test_dynamicclassattribute.py | 304 |
1 files changed, 304 insertions, 0 deletions
diff --git a/Lib/test/test_dynamicclassattribute.py b/Lib/test/test_dynamicclassattribute.py new file mode 100644 index 0000000..079d6c3 --- /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): + pass + +class PropertyGet(PropertyBase): + pass + +class PropertySet(PropertyBase): + pass + +class PropertyDel(PropertyBase): + pass + +class BaseClass(object): + def __init__(self): + self._spam = 5 + + @DynamicClassAttribute + def spam(self): + """BaseClass.getter""" + return self._spam + + @spam.setter + def spam(self, value): + self._spam = value + + @spam.deleter + def spam(self): + del self._spam + +class SubClass(BaseClass): + + spam = BaseClass.__dict__['spam'] + + @spam.getter + def spam(self): + """SubClass.getter""" + raise PropertyGet(self._spam) + + @spam.setter + def spam(self, value): + raise PropertySet(self._spam) + + @spam.deleter + def spam(self): + raise PropertyDel(self._spam) + +class PropertyDocBase(object): + _spam = 1 + def _get_spam(self): + return self._spam + 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""" + return self._spam + +class PropertySubNewGetter(BaseClass): + spam = BaseClass.__dict__['spam'] + @spam.getter + def spam(self): + """new docstring""" + return 5 + +class PropertyNewGetter(object): + @DynamicClassAttribute + def spam(self): + """original docstring""" + return 1 + @spam.getter + def spam(self): + """new docstring""" + return 8 + +class ClassWithAbstractVirtualProperty(metaclass=abc.ABCMeta): + @DynamicClassAttribute + @abc.abstractmethod + def color(): + pass + +class ClassWithPropertyAbstractVirtual(metaclass=abc.ABCMeta): + @abc.abstractmethod + @DynamicClassAttribute + def color(): + pass + +class PropertyTests(unittest.TestCase): + def test_property_decorator_baseclass(self): + # see #1620 + base = BaseClass() + self.assertEqual(base.spam, 5) + self.assertEqual(base._spam, 5) + base.spam = 10 + self.assertEqual(base.spam, 10) + self.assertEqual(base._spam, 10) + delattr(base, "spam") + self.assertTrue(not hasattr(base, "spam")) + self.assertTrue(not hasattr(base, "_spam")) + base.spam = 20 + self.assertEqual(base.spam, 20) + self.assertEqual(base._spam, 20) + + def test_property_decorator_subclass(self): + # see #1620 + sub = SubClass() + self.assertRaises(PropertyGet, getattr, sub, "spam") + self.assertRaises(PropertySet, setattr, sub, "spam", None) + self.assertRaises(PropertyDel, delattr, sub, "spam") + + @unittest.skipIf(sys.flags.optimize >= 2, + "Docstrings are omitted with -O2 and above") + def test_property_decorator_subclass_doc(self): + sub = SubClass() + self.assertEqual(sub.__class__.__dict__['spam'].__doc__, "SubClass.getter") + + @unittest.skipIf(sys.flags.optimize >= 2, + "Docstrings are omitted with -O2 and above") + def test_property_decorator_baseclass_doc(self): + base = BaseClass() + self.assertEqual(base.__class__.__dict__['spam'].__doc__, "BaseClass.getter") + + def test_property_decorator_doc(self): + base = PropertyDocBase() + sub = PropertyDocSub() + self.assertEqual(base.__class__.__dict__['spam'].__doc__, "spam spam spam") + self.assertEqual(sub.__class__.__dict__['spam'].__doc__, "spam spam spam") + + @unittest.skipIf(sys.flags.optimize >= 2, + "Docstrings are omitted with -O2 and above") + def test_property_getter_doc_override(self): + newgettersub = PropertySubNewGetter() + self.assertEqual(newgettersub.spam, 5) + self.assertEqual(newgettersub.__class__.__dict__['spam'].__doc__, "new docstring") + newgetter = PropertyNewGetter() + self.assertEqual(newgetter.spam, 8) + self.assertEqual(newgetter.__class__.__dict__['spam'].__doc__, "new docstring") + + def test_property___isabstractmethod__descriptor(self): + for val in (True, False, [], [1], '', '1'): + class C(object): + def foo(self): + pass + foo.__isabstractmethod__ = val + foo = DynamicClassAttribute(foo) + self.assertIs(C.__dict__['foo'].__isabstractmethod__, bool(val)) + + # check that the DynamicClassAttribute's __isabstractmethod__ descriptor does the + # right thing when presented with a value that fails truth testing: + class NotBool(object): + def __nonzero__(self): + raise ValueError() + __len__ = __nonzero__ + with self.assertRaises(ValueError): + class C(object): + def foo(self): + pass + foo.__isabstractmethod__ = NotBool() + foo = DynamicClassAttribute(foo) + + def test_abstract_virtual(self): + self.assertRaises(TypeError, ClassWithAbstractVirtualProperty) + self.assertRaises(TypeError, ClassWithPropertyAbstractVirtual) + class APV(ClassWithPropertyAbstractVirtual): + pass + self.assertRaises(TypeError, APV) + class AVP(ClassWithAbstractVirtualProperty): + pass + self.assertRaises(TypeError, AVP) + class Okay1(ClassWithAbstractVirtualProperty): + @DynamicClassAttribute + def color(self): + return self._color + def __init__(self): + self._color = 'cyan' + with self.assertRaises(AttributeError): + Okay1.color + self.assertEqual(Okay1().color, 'cyan') + class Okay2(ClassWithAbstractVirtualProperty): + @DynamicClassAttribute + def color(self): + return self._color + def __init__(self): + self._color = 'magenta' + with self.assertRaises(AttributeError): + Okay2.color + self.assertEqual(Okay2().color, 'magenta') + + +# Issue 5890: subclasses of DynamicClassAttribute do not preserve method __doc__ strings +class PropertySub(DynamicClassAttribute): + """This is a subclass of DynamicClassAttribute""" + +class PropertySubSlots(DynamicClassAttribute): + """This is a subclass of DynamicClassAttribute that defines __slots__""" + __slots__ = () + +class PropertySubclassTests(unittest.TestCase): + + @unittest.skipIf(hasattr(PropertySubSlots, '__doc__'), + "__doc__ is already present, __slots__ will have no effect") + def test_slots_docstring_copy_exception(self): + try: + class Foo(object): + @PropertySubSlots + def spam(self): + """Trying to copy this docstring will raise an exception""" + return 1 + print('\n',spam.__doc__) + except AttributeError: + pass + else: + raise Exception("AttributeError not raised") + + @unittest.skipIf(sys.flags.optimize >= 2, + "Docstrings are omitted with -O2 and above") + def test_docstring_copy(self): + class Foo(object): + @PropertySub + def spam(self): + """spam wrapped in DynamicClassAttribute subclass""" + return 1 + self.assertEqual( + Foo.__dict__['spam'].__doc__, + "spam wrapped in DynamicClassAttribute subclass") + + @unittest.skipIf(sys.flags.optimize >= 2, + "Docstrings are omitted with -O2 and above") + def test_property_setter_copies_getter_docstring(self): + class Foo(object): + def __init__(self): self._spam = 1 + @PropertySub + def spam(self): + """spam wrapped in DynamicClassAttribute subclass""" + return self._spam + @spam.setter + def spam(self, value): + """this docstring is ignored""" + self._spam = value + foo = Foo() + self.assertEqual(foo.spam, 1) + foo.spam = 2 + self.assertEqual(foo.spam, 2) + self.assertEqual( + Foo.__dict__['spam'].__doc__, + "spam wrapped in DynamicClassAttribute subclass") + class FooSub(Foo): + spam = Foo.__dict__['spam'] + @spam.setter + def spam(self, value): + """another ignored docstring""" + self._spam = 'eggs' + foosub = FooSub() + self.assertEqual(foosub.spam, 1) + foosub.spam = 7 + self.assertEqual(foosub.spam, 'eggs') + self.assertEqual( + FooSub.__dict__['spam'].__doc__, + "spam wrapped in DynamicClassAttribute subclass") + + @unittest.skipIf(sys.flags.optimize >= 2, + "Docstrings are omitted with -O2 and above") + def test_property_new_getter_new_docstring(self): + + class Foo(object): + @PropertySub + def spam(self): + """a docstring""" + return 1 + @spam.getter + def spam(self): + """a new docstring""" + return 2 + self.assertEqual(Foo.__dict__['spam'].__doc__, "a new docstring") + class FooBase(object): + @PropertySub + def spam(self): + """a docstring""" + return 1 + class Foo2(FooBase): + spam = FooBase.__dict__['spam'] + @spam.getter + def spam(self): + """a new docstring""" + return 2 + self.assertEqual(Foo.__dict__['spam'].__doc__, "a new docstring") + + + +def test_main(): + run_unittest(PropertyTests, PropertySubclassTests) + +if __name__ == '__main__': + test_main() |