diff -r 3ee046d44287 Doc/library/abc.rst --- a/Doc/library/abc.rst Sun Mar 20 11:37:13 2011 -0400 +++ b/Doc/library/abc.rst Sun Mar 20 13:57:10 2011 -0400 @@ -198,28 +198,58 @@ A subclass of the built-in :func:`property`, indicating an abstract property. - Using this function requires that the class's metaclass is :class:`ABCMeta` or - is derived from it. - A class that has a metaclass derived from :class:`ABCMeta` cannot be - instantiated unless all of its abstract methods and properties are overridden. - The abstract properties can be called using any of the normal - 'super' call mechanisms. + Using this function requires that the class's metaclass is :class:`ABCMeta` + or is derived from it. A class that has a metaclass derived from + :class:`ABCMeta` cannot be instantiated unless all of its abstract methods + and properties are overridden. The abstract properties can be called using + any of the normal 'super' call mechanisms. Usage:: class C(metaclass=ABCMeta): @abstractproperty - def my_abstract_property(self): + @abstractmethod + def x(self): + ... + @x.setter + def x(self, val): ... - This defines a read-only property; you can also define a read-write abstract - property using the 'long' form of property declaration:: + You can also define an abstract property using the 'long' form of + property declaration:: - class C(metaclass=ABCMeta): - def getx(self): ... - def setx(self, value): ... + class D(metaclass=ABCMeta): + @abstractmethod + def getx(self): + ... + @abstractmethod + def setx(self, value): + ... x = abstractproperty(getx, setx) + Subclasses will not be instantiable until the property definition + is fully concrete:: + + class E(C): + @C.x.getter + def x(self): + ... + class F(E): + @D.x.setter + def x(self, val): + ... + + In this example, 'E' is not instantiable, because the 'x' setter + is abstract. 'F.x' has replaced all of its abstract methods with + concrete ones, thus it is a regular property , and 'F' is + instantiable. + + + .. versionchanged:: 3.3 + Respects the abstractedness of functions passed to + :func:`abstractproperty.getter`, :func:`abstractproperty.setter` + and :func:`abstractproperty.deleter`. + .. rubric:: Footnotes diff -r 3ee046d44287 Lib/abc.py --- a/Lib/abc.py Sun Mar 20 11:37:13 2011 -0400 +++ b/Lib/abc.py Sun Mar 20 13:57:10 2011 -0400 @@ -78,19 +78,68 @@ class C(metaclass=ABCMeta): @abstractproperty - def my_abstract_property(self): + @abstractmethod + def x(self): + ... + @x.setter + def x(self, val): ... - This defines a read-only property; you can also define a read-write - abstract property using the 'long' form of property declaration: + You can also define an abstract property using the 'long' form of + property declaration: - class C(metaclass=ABCMeta): - def getx(self): ... - def setx(self, value): ... + class D(metaclass=ABCMeta): + @abstractmethod + def getx(self): + ... + @abstractmethod + def setx(self, value): + ... x = abstractproperty(getx, setx) + + Subclasses will not be instantiable until the property definition + is fully concrete: + + class E(C): + @C.x.getter + def x(self): + ... + + class F(E): + @D.x.setter + def x(self, val): + ... + + In this example, 'E' is not instantiable, because the 'x' setter + is abstract. 'F.x' is a regular property , and 'F' is instantiable. """ + __isabstractmethod__ = True + def getter(self, fun): + "Descriptor to change the getter on a property" + return abstractproperty.create_property(fun, self.fset, self.fdel, + self.__doc__) + + def setter(self, fun): + "Descriptor to change the setter on a property" + return abstractproperty.create_property(self.fget, fun, self.fdel, + self.__doc__) + + def deleter(self, fun): + "Descriptor to change the deleter on a property" + return abstractproperty.create_property(self.fget, self.fset, fun, + self.__doc__) + + @classmethod + def create_property(cls, fget=None, fset=None, fdel=None, doc=None): + for f in (fget, fset, fdel): + if (f is not None) and getattr(f, '__isabstractmethod__', False): + # has abstract methods, return an abstract property + return cls(fget, fset, fdel, doc) + # does not have abstract methods, return a concrete property + return property(fget, fset, fdel, doc) + class ABCMeta(type): diff -r 3ee046d44287 Lib/test/test_abc.py --- a/Lib/test/test_abc.py Sun Mar 20 11:37:13 2011 -0400 +++ b/Lib/test/test_abc.py Sun Mar 20 13:57:10 2011 -0400 @@ -29,11 +29,33 @@ class C(metaclass=abc.ABCMeta): @abc.abstractproperty def foo(self): return 3 + self.assertRaises(TypeError, C) class D(C): @property def foo(self): return super().foo self.assertEqual(D().foo, 3) + def test_abstractproperty_with_subclassing(self): + class C(metaclass=abc.ABCMeta): + @abc.abstractproperty + @abc.abstractmethod + def foo(self): return 3 + @foo.setter + @abc.abstractmethod + def foo(self, val): pass + self.assertRaises(TypeError, C) + class D(C): + @C.foo.getter + def foo(self): return super().foo + self.assertRaises(TypeError, D) + self.assertTrue(isinstance(D.foo, abc.abstractproperty)) + class E(D): + @D.foo.setter + def foo(self, val): pass + self.assertEqual(E().foo, 3) + self.assertFalse(isinstance(E.foo, abc.abstractproperty)) + self.assertTrue(isinstance(E.foo, property)) + def test_abstractclassmethod_basics(self): @abc.abstractclassmethod def foo(cls): pass