diff -r 74d182cf0187 Doc/library/abc.rst --- a/Doc/library/abc.rst Wed Nov 30 00:25:06 2011 +0200 +++ b/Doc/library/abc.rst Mon Dec 05 13:21:56 2011 -0500 @@ -127,19 +127,18 @@ available as a method of ``Foo``, so it is provided separately. -It also provides the following decorators: +The abc module also provides the following decorators: .. decorator:: abstractmethod(function) A decorator indicating abstract methods. - Using this decorator 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 methods can be called using any of the normal 'super' call - mechanisms. + Using this decorator 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 methods can be called using any + of the normal 'super' call mechanisms. :func:`abstractmethod` may be used + to declare abstract methods for properties and descriptors. Dynamically adding abstract methods to a class, or attempting to modify the abstraction status of a method or class once it is created, are not @@ -153,6 +152,45 @@ @abstractmethod def my_abstract_method(self, ...): ... + @classmethod + @abstractmethod + def my_abstract_classmethod(self, ...): + ... + @staticmethod + @abstractmethod + def my_abstract_staticmethod(self, ...): + ... + + @property + @abstractmethod + def my_abstract_property(self): + ... + @my_abstract_property.setter + @abstractmethod + def my_abstract_property(self, val): + ... + + @abstractmethod + def _get_x(self): + ... + @abstractmethod + def _set_x(self, val): + ... + x = property(_get_x, _set_x) + + + In order to include abc support for custom descriptors, the descriptor + must identify itself as abstract using :attr:`__isabstractmethod__`. For + example, Python's built-in property does the equivalent of:: + + class Descriptor: + ... + @property + def __isabstractmethod__(self): + for f in (self._fget, self._fset, self._fdel): + if getattr(f, '__isabstractmethod__', False): + return True + return False .. note:: @@ -166,59 +204,20 @@ .. decorator:: abstractclassmethod(function) - A subclass of the built-in :func:`classmethod`, indicating an abstract - classmethod. Otherwise it is similar to :func:`abstractmethod`. - - Usage:: - - class C(metaclass=ABCMeta): - @abstractclassmethod - def my_abstract_classmethod(cls, ...): - ... - - .. versionadded:: 3.2 + .. deprecated:: 3.3 + Use :class:`classmethod` with :func:`abstractmethod` instead .. decorator:: abstractstaticmethod(function) - A subclass of the built-in :func:`staticmethod`, indicating an abstract - staticmethod. Otherwise it is similar to :func:`abstractmethod`. - - Usage:: - - class C(metaclass=ABCMeta): - @abstractstaticmethod - def my_abstract_staticmethod(...): - ... - - .. versionadded:: 3.2 + .. deprecated:: 3.3 + Use :class:`staticmethod` with :func:`abstractmethod` instead .. decorator:: abstractproperty(fget=None, fset=None, fdel=None, doc=None) - 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. - - Usage:: - - class C(metaclass=ABCMeta): - @abstractproperty - def my_abstract_property(self): - ... - - This defines a read-only property; you can also define a read-write abstract - property using the 'long' form of property declaration:: - - class C(metaclass=ABCMeta): - def getx(self): ... - def setx(self, value): ... - x = abstractproperty(getx, setx) + .. deprecated:: 3.3 + Use :class:`property` with :func:`abstractmethod` instead .. rubric:: Footnotes diff -r 74d182cf0187 Doc/whatsnew/3.3.rst --- a/Doc/whatsnew/3.3.rst Wed Nov 30 00:25:06 2011 +0200 +++ b/Doc/whatsnew/3.3.rst Mon Dec 05 13:21:56 2011 -0500 @@ -352,6 +352,20 @@ (Contributed by IƱigo Serna in :issue:`6755`) +abc +--- + +Improved support for abstract base classes containing descriptors composed with +abstract methods. + + * :class:`abc.abstractproperty` has been deprecated, use :class:`property` + with :func:`abc.abstractmethod` instead. + * :class:`abc.abstractclassmethod` has been deprecated, use + :class:`classmethod` with :func:`abc.abstractmethod` instead. + * :class:`abc.abstractstaticmethod` has been deprecated, use + :class:`property` with :func:`abc.abstractmethod` instead. + + faulthandler ------------ diff -r 74d182cf0187 Include/object.h --- a/Include/object.h Wed Nov 30 00:25:06 2011 +0200 +++ b/Include/object.h Mon Dec 05 13:21:56 2011 -0500 @@ -473,6 +473,7 @@ PyAPI_FUNC(PyObject *) PyObject_GetAttr(PyObject *, PyObject *); PyAPI_FUNC(int) PyObject_SetAttr(PyObject *, PyObject *, PyObject *); PyAPI_FUNC(int) PyObject_HasAttr(PyObject *, PyObject *); +PyAPI_FUNC(int) _PyObject_IsAbstract(PyObject *); PyAPI_FUNC(PyObject *) _PyObject_GetAttrId(PyObject *, struct _Py_Identifier *); PyAPI_FUNC(int) _PyObject_SetAttrId(PyObject *, struct _Py_Identifier *, PyObject *); PyAPI_FUNC(int) _PyObject_HasAttrId(PyObject *, struct _Py_Identifier *); diff -r 74d182cf0187 Lib/abc.py --- a/Lib/abc.py Wed Nov 30 00:25:06 2011 +0200 +++ b/Lib/abc.py Mon Dec 05 13:21:56 2011 -0500 @@ -20,13 +20,42 @@ @abstractmethod def my_abstract_method(self, ...): ... + @classmethod + @abstractmethod + def my_abstract_classmethod(self, ...): + ... + @staticmethod + @abstractmethod + def my_abstract_staticmethod(self, ...): + ... + + @property + @abstractmethod + def my_abstract_property(self): + ... + @my_abstract_property.setter + @abstractmethod + def my_abstract_property(self, val): + ... + + @abstractmethod + def _get_x(self): + ... + @abstractmethod + def _set_x(self, val): + ... + x = property(_get_x, _set_x) """ funcobj.__isabstractmethod__ = True return funcobj class abstractclassmethod(classmethod): - """A decorator indicating abstract classmethods. + """ + "abstractclassmethod" is deprecated. Use "classmethod" with + "abstractmethod" instead. + + A decorator indicating abstract classmethods. Similar to abstractmethod. @@ -41,12 +70,19 @@ __isabstractmethod__ = True def __init__(self, callable): +# import warnings +# warnings.warn("abstractclassmethod is deprecated", +# DeprecationWarning, 2) callable.__isabstractmethod__ = True super().__init__(callable) class abstractstaticmethod(staticmethod): - """A decorator indicating abstract staticmethods. + """ + "abstractstaticmethod" is deprecated. Use "staticmethod" with + "abstractmethod" instead. + + A decorator indicating abstract staticmethods. Similar to abstractmethod. @@ -61,12 +97,18 @@ __isabstractmethod__ = True def __init__(self, callable): +# import warnings +# warnings.warn("abstractstaticmethod is deprecated", +# DeprecationWarning, 2) callable.__isabstractmethod__ = True super().__init__(callable) +class abstractproperty(property): + """ + "abstractproperty" is deprecated. Use "property" with "abstractmethod" + instead. -class abstractproperty(property): - """A decorator indicating abstract properties. + A decorator indicating abstract properties. Requires that the metaclass is ABCMeta or derived from it. A class that has a metaclass derived from ABCMeta cannot be @@ -88,9 +130,15 @@ def getx(self): ... def setx(self, value): ... x = abstractproperty(getx, setx) + """ __isabstractmethod__ = True + # def __init__(self, *args, **kwargs): + # import warnings + # warnings.warn("abstractproperty is deprecated", DeprecationWarning, 2) + # super().__init__(*args, **kwargs) + class ABCMeta(type): diff -r 74d182cf0187 Lib/numbers.py --- a/Lib/numbers.py Wed Nov 30 00:25:06 2011 +0200 +++ b/Lib/numbers.py Mon Dec 05 13:21:56 2011 -0500 @@ -5,7 +5,7 @@ TODO: Fill out more detailed documentation on the operators.""" -from abc import ABCMeta, abstractmethod, abstractproperty +from abc import ABCMeta, abstractmethod __all__ = ["Number", "Complex", "Real", "Rational", "Integral"] @@ -50,7 +50,8 @@ """True if self != 0. Called for bool(self).""" return self != 0 - @abstractproperty + @property + @abstractmethod def real(self): """Retrieve the real component of this number. @@ -58,7 +59,8 @@ """ raise NotImplementedError - @abstractproperty + @property + @abstractmethod def imag(self): """Retrieve the imaginary component of this number. @@ -272,11 +274,13 @@ __slots__ = () - @abstractproperty + @property + @abstractmethod def numerator(self): raise NotImplementedError - @abstractproperty + @property + @abstractmethod def denominator(self): raise NotImplementedError diff -r 74d182cf0187 Lib/test/test_abc.py --- a/Lib/test/test_abc.py Wed Nov 30 00:25:06 2011 +0200 +++ b/Lib/test/test_abc.py Mon Dec 05 13:21:56 2011 -0500 @@ -17,33 +17,38 @@ def foo(self): pass self.assertTrue(foo.__isabstractmethod__) def bar(self): pass - self.assertFalse(hasattr(bar, "__isabstractmethod__")) + self.assertFalse(getattr(bar, "__isabstractmethod__", False)) def test_abstractproperty_basics(self): - @abc.abstractproperty + @property + @abc.abstractmethod def foo(self): pass self.assertTrue(foo.__isabstractmethod__) def bar(self): pass - self.assertFalse(hasattr(bar, "__isabstractmethod__")) + self.assertFalse(getattr(bar, "__isabstractmethod__", False)) class C(metaclass=abc.ABCMeta): - @abc.abstractproperty + @property + @abc.abstractmethod def foo(self): return 3 + self.assertRaises(TypeError, C) class D(C): - @property + @C.foo.getter def foo(self): return super().foo self.assertEqual(D().foo, 3) def test_abstractclassmethod_basics(self): - @abc.abstractclassmethod + @classmethod + @abc.abstractmethod def foo(cls): pass self.assertTrue(foo.__isabstractmethod__) @classmethod def bar(cls): pass - self.assertFalse(hasattr(bar, "__isabstractmethod__")) + self.assertFalse(getattr(bar, "__isabstractmethod__", False)) class C(metaclass=abc.ABCMeta): - @abc.abstractclassmethod + @classmethod + @abc.abstractmethod def foo(cls): return cls.__name__ self.assertRaises(TypeError, C) class D(C): @@ -53,15 +58,17 @@ self.assertEqual(D().foo(), 'D') def test_abstractstaticmethod_basics(self): - @abc.abstractstaticmethod + @staticmethod + @abc.abstractmethod def foo(): pass self.assertTrue(foo.__isabstractmethod__) @staticmethod def bar(): pass - self.assertFalse(hasattr(bar, "__isabstractmethod__")) + self.assertFalse(getattr(bar, "__isabstractmethod__", False)) class C(metaclass=abc.ABCMeta): - @abc.abstractstaticmethod + @staticmethod + @abc.abstractmethod def foo(): return 3 self.assertRaises(TypeError, C) class D(C): @@ -98,6 +105,67 @@ self.assertRaises(TypeError, F) # because bar is abstract now self.assertTrue(isabstract(F)) + def test_descriptors_with_abstractmethod(self): + class C(metaclass=abc.ABCMeta): + @property + @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) + class E(D): + @D.foo.setter + def foo(self, val): pass + self.assertEqual(E().foo, 3) + # check that the property'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 F(C): + def bar(self): + pass + bar.__isabstractmethod__ = NotBool() + foo = property(bar) + + + def test_customdescriptors_with_abstractmethod(self): + class Descriptor: + def __init__(self, fget, fset=None): + self._fget = fget + self._fset = fset + def getter(self, callable): + return Descriptor(callable, self._fget) + def setter(self, callable): + return Descriptor(self._fget, callable) + @property + def __isabstractmethod__(self): + return (getattr(self._fget, '__isabstractmethod__', False) + or getattr(self._fset, '__isabstractmethod__', False)) + class C(metaclass=abc.ABCMeta): + @Descriptor + @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) + class E(D): + @D.foo.setter + def foo(self, val): pass + self.assertFalse(E.foo.__isabstractmethod__) + def test_metaclass_abc(self): # Metaclasses can be ABCs, too. class A(metaclass=abc.ABCMeta): diff -r 74d182cf0187 Lib/test/test_property.py --- a/Lib/test/test_property.py Wed Nov 30 00:25:06 2011 +0200 +++ b/Lib/test/test_property.py Mon Dec 05 13:21:56 2011 -0500 @@ -128,6 +128,30 @@ self.assertEqual(newgetter.spam, 8) self.assertEqual(newgetter.__class__.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 = property(foo) + self.assertIs(C.foo.__isabstractmethod__, bool(val)) + + # check that the property'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 = property(foo) + C.foo.__isabstractmethod__ + + # Issue 5890: subclasses of property do not preserve method __doc__ strings class PropertySub(property): diff -r 74d182cf0187 Misc/ACKS --- a/Misc/ACKS Wed Nov 30 00:25:06 2011 +0200 +++ b/Misc/ACKS Mon Dec 05 13:21:56 2011 -0500 @@ -220,6 +220,7 @@ Antonio Cuni Brian Curtin Lisandro Dalcin +Darren Dale Andrew Dalke Lars Damerow Evan Dandrea diff -r 74d182cf0187 Objects/descrobject.c --- a/Objects/descrobject.c Wed Nov 30 00:25:06 2011 +0200 +++ b/Objects/descrobject.c Mon Dec 05 13:21:56 2011 -0500 @@ -1329,6 +1329,38 @@ return 0; } +static PyObject * +property_get___isabstractmethod__(propertyobject *prop, void *closure) +{ + int res = _PyObject_IsAbstract(prop->prop_get); + if (res == -1) + return NULL; + else if (res) + Py_RETURN_TRUE; + + res = _PyObject_IsAbstract(prop->prop_set); + if (res == -1) + return NULL; + else if (res) + Py_RETURN_TRUE; + + res = _PyObject_IsAbstract(prop->prop_del); + if (res == -1) + return NULL; + else if (res) + Py_RETURN_TRUE; + + Py_RETURN_FALSE; +} + +static PyGetSetDef property_getsetlist[] = { + {"__isabstractmethod__", + (getter)property_get___isabstractmethod__, NULL, + NULL, + NULL}, + {NULL} /* Sentinel */ +}; + PyDoc_STRVAR(property_doc, "property(fget=None, fset=None, fdel=None, doc=None) -> property attribute\n" "\n" @@ -1394,7 +1426,7 @@ 0, /* tp_iternext */ property_methods, /* tp_methods */ property_members, /* tp_members */ - 0, /* tp_getset */ + property_getsetlist, /* tp_getset */ 0, /* tp_base */ 0, /* tp_dict */ property_descr_get, /* tp_descr_get */ diff -r 74d182cf0187 Objects/funcobject.c --- a/Objects/funcobject.c Wed Nov 30 00:25:06 2011 +0200 +++ b/Objects/funcobject.c Mon Dec 05 13:21:56 2011 -0500 @@ -814,6 +814,26 @@ {NULL} /* Sentinel */ }; +static PyObject * +cm_get___isabstractmethod__(classmethod *cm, void *closure) +{ + int res = _PyObject_IsAbstract(cm->cm_callable); + if (res == -1) + return NULL; + else if (res) + Py_RETURN_TRUE; + + Py_RETURN_FALSE; +} + +static PyGetSetDef cm_getsetlist[] = { + {"__isabstractmethod__", + (getter)cm_get___isabstractmethod__, NULL, + NULL, + NULL}, + {NULL} /* Sentinel */ +}; + PyDoc_STRVAR(classmethod_doc, "classmethod(function) -> method\n\ \n\ @@ -865,7 +885,7 @@ 0, /* tp_iternext */ 0, /* tp_methods */ cm_memberlist, /* tp_members */ - 0, /* tp_getset */ + cm_getsetlist, /* tp_getset */ 0, /* tp_base */ 0, /* tp_dict */ cm_descr_get, /* tp_descr_get */ @@ -969,6 +989,26 @@ {NULL} /* Sentinel */ }; +static PyObject * +sm_get___isabstractmethod__(staticmethod *sm, void *closure) +{ + int res = _PyObject_IsAbstract(sm->sm_callable); + if (res == -1) + return NULL; + else if (res) + Py_RETURN_TRUE; + + Py_RETURN_FALSE; +} + +static PyGetSetDef sm_getsetlist[] = { + {"__isabstractmethod__", + (getter)sm_get___isabstractmethod__, NULL, + NULL, + NULL}, + {NULL} /* Sentinel */ +}; + PyDoc_STRVAR(staticmethod_doc, "staticmethod(function) -> method\n\ \n\ @@ -1017,7 +1057,7 @@ 0, /* tp_iternext */ 0, /* tp_methods */ sm_memberlist, /* tp_members */ - 0, /* tp_getset */ + sm_getsetlist, /* tp_getset */ 0, /* tp_base */ 0, /* tp_dict */ sm_descr_get, /* tp_descr_get */ diff -r 74d182cf0187 Objects/object.c --- a/Objects/object.c Wed Nov 30 00:25:06 2011 +0200 +++ b/Objects/object.c Mon Dec 05 13:21:56 2011 -0500 @@ -830,6 +830,29 @@ return res; } +int +_PyObject_IsAbstract(PyObject *obj) +{ + int res; + PyObject* isabstract; + _Py_IDENTIFIER(__isabstractmethod__); + + if (obj == NULL) + return 0; + + isabstract = _PyObject_GetAttrId(obj, &PyId___isabstractmethod__); + if (isabstract == NULL) { + if (PyErr_ExceptionMatches(PyExc_AttributeError)) { + PyErr_Clear(); + return 0; + } + return -1; + } + res = PyObject_IsTrue(isabstract); + Py_DECREF(isabstract); + return res; +} + PyObject * _PyObject_GetAttrId(PyObject *v, _Py_Identifier *name) {