Commit bfebb7b5 authored by Benjamin Peterson's avatar Benjamin Peterson

improve abstract property support (closes #11610)

Thanks to Darren Dale for patch.
parent a8ff01ca
...@@ -127,19 +127,18 @@ This module provides the following class: ...@@ -127,19 +127,18 @@ This module provides the following class:
available as a method of ``Foo``, so it is provided separately. available as a method of ``Foo``, so it is provided separately.
It also provides the following decorators: The :mod:`abc` module also provides the following decorators:
.. decorator:: abstractmethod(function) .. decorator:: abstractmethod(function)
A decorator indicating abstract methods. A decorator indicating abstract methods.
Using this decorator requires that the class's metaclass is :class:`ABCMeta` or Using this decorator requires that the class's metaclass is :class:`ABCMeta`
is derived from it. or is derived from it. A class that has a metaclass derived from
A class that has a metaclass derived from :class:`ABCMeta` :class:`ABCMeta` cannot be instantiated unless all of its abstract methods
cannot be instantiated unless all of its abstract methods and and properties are overridden. The abstract methods can be called using any
properties are overridden. of the normal 'super' call mechanisms. :func:`abstractmethod` may be used
The abstract methods can be called using any of the normal 'super' call to declare abstract methods for properties and descriptors.
mechanisms.
Dynamically adding abstract methods to a class, or attempting to modify the 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 abstraction status of a method or class once it is created, are not
...@@ -147,12 +146,52 @@ It also provides the following decorators: ...@@ -147,12 +146,52 @@ It also provides the following decorators:
regular inheritance; "virtual subclasses" registered with the ABC's regular inheritance; "virtual subclasses" registered with the ABC's
:meth:`register` method are not affected. :meth:`register` method are not affected.
Usage:: When :func:`abstractmethod` is applied in combination with other method
descriptors, it should be applied as the innermost decorator, as shown in
the following usage examples::
class C(metaclass=ABCMeta): class C(metaclass=ABCMeta):
@abstractmethod @abstractmethod
def my_abstract_method(self, ...): def my_abstract_method(self, ...):
... ...
@classmethod
@abstractmethod
def my_abstract_classmethod(cls, ...):
...
@staticmethod
@abstractmethod
def my_abstract_staticmethod(...):
...
@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 correctly interoperate with the abstract base class machinery,
the descriptor must identify itself as abstract using
:attr:`__isabstractmethod__`. In general, this attribute should be ``True``
if any of the methods used to compose the descriptor are abstract. For
example, Python's built-in property does the equivalent of::
class Descriptor:
...
@property
def __isabstractmethod__(self):
return any(getattr(f, '__isabstractmethod__', False) for
f in (self._fget, self._fset, self._fdel))
.. note:: .. note::
...@@ -177,6 +216,8 @@ It also provides the following decorators: ...@@ -177,6 +216,8 @@ It also provides the following decorators:
... ...
.. versionadded:: 3.2 .. versionadded:: 3.2
.. deprecated:: 3.3
Use :class:`classmethod` with :func:`abstractmethod` instead
.. decorator:: abstractstaticmethod(function) .. decorator:: abstractstaticmethod(function)
...@@ -192,18 +233,19 @@ It also provides the following decorators: ...@@ -192,18 +233,19 @@ It also provides the following decorators:
... ...
.. versionadded:: 3.2 .. versionadded:: 3.2
.. deprecated:: 3.3
Use :class:`staticmethod` with :func:`abstractmethod` instead
.. decorator:: abstractproperty(fget=None, fset=None, fdel=None, doc=None) .. decorator:: abstractproperty(fget=None, fset=None, fdel=None, doc=None)
A subclass of the built-in :func:`property`, indicating an abstract property. 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 Using this function requires that the class's metaclass is :class:`ABCMeta`
is derived from it. or is derived from it. A class that has a metaclass derived from
A class that has a metaclass derived from :class:`ABCMeta` cannot be :class:`ABCMeta` cannot be instantiated unless all of its abstract methods
instantiated unless all of its abstract methods and properties are overridden. and properties are overridden. The abstract properties can be called using
The abstract properties can be called using any of the normal any of the normal 'super' call mechanisms.
'super' call mechanisms.
Usage:: Usage::
...@@ -220,6 +262,9 @@ It also provides the following decorators: ...@@ -220,6 +262,9 @@ It also provides the following decorators:
def setx(self, value): ... def setx(self, value): ...
x = abstractproperty(getx, setx) x = abstractproperty(getx, setx)
.. deprecated:: 3.3
Use :class:`property` with :func:`abstractmethod` instead
.. rubric:: Footnotes .. rubric:: Footnotes
......
...@@ -352,6 +352,23 @@ curses ...@@ -352,6 +352,23 @@ curses
(Contributed by Iñigo Serna in :issue:`6755`) (Contributed by Iñigo Serna in :issue:`6755`)
abc
---
Improved support for abstract base classes containing descriptors composed with
abstract methods. The recommended approach to declaring abstract descriptors is
now to provide :attr:`__isabstractmethod__` as a dynamically updated
property. The built-in descriptors have been updated accordingly.
* :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.
(Contributed by Darren Dale in :issue:`11610`)
faulthandler faulthandler
------------ ------------
......
...@@ -473,6 +473,7 @@ PyAPI_FUNC(int) PyObject_HasAttrString(PyObject *, const char *); ...@@ -473,6 +473,7 @@ PyAPI_FUNC(int) PyObject_HasAttrString(PyObject *, const char *);
PyAPI_FUNC(PyObject *) PyObject_GetAttr(PyObject *, PyObject *); PyAPI_FUNC(PyObject *) PyObject_GetAttr(PyObject *, PyObject *);
PyAPI_FUNC(int) PyObject_SetAttr(PyObject *, PyObject *, PyObject *); PyAPI_FUNC(int) PyObject_SetAttr(PyObject *, PyObject *, PyObject *);
PyAPI_FUNC(int) PyObject_HasAttr(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(PyObject *) _PyObject_GetAttrId(PyObject *, struct _Py_Identifier *);
PyAPI_FUNC(int) _PyObject_SetAttrId(PyObject *, struct _Py_Identifier *, PyObject *); PyAPI_FUNC(int) _PyObject_SetAttrId(PyObject *, struct _Py_Identifier *, PyObject *);
PyAPI_FUNC(int) _PyObject_HasAttrId(PyObject *, struct _Py_Identifier *); PyAPI_FUNC(int) _PyObject_HasAttrId(PyObject *, struct _Py_Identifier *);
......
...@@ -26,7 +26,8 @@ def abstractmethod(funcobj): ...@@ -26,7 +26,8 @@ def abstractmethod(funcobj):
class abstractclassmethod(classmethod): class abstractclassmethod(classmethod):
"""A decorator indicating abstract classmethods. """
A decorator indicating abstract classmethods.
Similar to abstractmethod. Similar to abstractmethod.
...@@ -36,6 +37,9 @@ class abstractclassmethod(classmethod): ...@@ -36,6 +37,9 @@ class abstractclassmethod(classmethod):
@abstractclassmethod @abstractclassmethod
def my_abstract_classmethod(cls, ...): def my_abstract_classmethod(cls, ...):
... ...
'abstractclassmethod' is deprecated. Use 'classmethod' with
'abstractmethod' instead.
""" """
__isabstractmethod__ = True __isabstractmethod__ = True
...@@ -46,7 +50,8 @@ class abstractclassmethod(classmethod): ...@@ -46,7 +50,8 @@ class abstractclassmethod(classmethod):
class abstractstaticmethod(staticmethod): class abstractstaticmethod(staticmethod):
"""A decorator indicating abstract staticmethods. """
A decorator indicating abstract staticmethods.
Similar to abstractmethod. Similar to abstractmethod.
...@@ -56,6 +61,9 @@ class abstractstaticmethod(staticmethod): ...@@ -56,6 +61,9 @@ class abstractstaticmethod(staticmethod):
@abstractstaticmethod @abstractstaticmethod
def my_abstract_staticmethod(...): def my_abstract_staticmethod(...):
... ...
'abstractstaticmethod' is deprecated. Use 'staticmethod' with
'abstractmethod' instead.
""" """
__isabstractmethod__ = True __isabstractmethod__ = True
...@@ -66,7 +74,8 @@ class abstractstaticmethod(staticmethod): ...@@ -66,7 +74,8 @@ class abstractstaticmethod(staticmethod):
class abstractproperty(property): class abstractproperty(property):
"""A decorator indicating abstract properties. """
A decorator indicating abstract properties.
Requires that the metaclass is ABCMeta or derived from it. A Requires that the metaclass is ABCMeta or derived from it. A
class that has a metaclass derived from ABCMeta cannot be class that has a metaclass derived from ABCMeta cannot be
...@@ -88,7 +97,11 @@ class abstractproperty(property): ...@@ -88,7 +97,11 @@ class abstractproperty(property):
def getx(self): ... def getx(self): ...
def setx(self, value): ... def setx(self, value): ...
x = abstractproperty(getx, setx) x = abstractproperty(getx, setx)
'abstractproperty' is deprecated. Use 'property' with 'abstractmethod'
instead.
""" """
__isabstractmethod__ = True __isabstractmethod__ = True
......
...@@ -5,7 +5,7 @@ ...@@ -5,7 +5,7 @@
TODO: Fill out more detailed documentation on the operators.""" 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"] __all__ = ["Number", "Complex", "Real", "Rational", "Integral"]
...@@ -50,7 +50,8 @@ class Complex(Number): ...@@ -50,7 +50,8 @@ class Complex(Number):
"""True if self != 0. Called for bool(self).""" """True if self != 0. Called for bool(self)."""
return self != 0 return self != 0
@abstractproperty @property
@abstractmethod
def real(self): def real(self):
"""Retrieve the real component of this number. """Retrieve the real component of this number.
...@@ -58,7 +59,8 @@ class Complex(Number): ...@@ -58,7 +59,8 @@ class Complex(Number):
""" """
raise NotImplementedError raise NotImplementedError
@abstractproperty @property
@abstractmethod
def imag(self): def imag(self):
"""Retrieve the imaginary component of this number. """Retrieve the imaginary component of this number.
...@@ -272,11 +274,13 @@ class Rational(Real): ...@@ -272,11 +274,13 @@ class Rational(Real):
__slots__ = () __slots__ = ()
@abstractproperty @property
@abstractmethod
def numerator(self): def numerator(self):
raise NotImplementedError raise NotImplementedError
@abstractproperty @property
@abstractmethod
def denominator(self): def denominator(self):
raise NotImplementedError raise NotImplementedError
......
...@@ -10,14 +10,7 @@ import abc ...@@ -10,14 +10,7 @@ import abc
from inspect import isabstract from inspect import isabstract
class TestABC(unittest.TestCase): class TestLegacyAPI(unittest.TestCase):
def test_abstractmethod_basics(self):
@abc.abstractmethod
def foo(self): pass
self.assertTrue(foo.__isabstractmethod__)
def bar(self): pass
self.assertFalse(hasattr(bar, "__isabstractmethod__"))
def test_abstractproperty_basics(self): def test_abstractproperty_basics(self):
@abc.abstractproperty @abc.abstractproperty
...@@ -29,10 +22,12 @@ class TestABC(unittest.TestCase): ...@@ -29,10 +22,12 @@ class TestABC(unittest.TestCase):
class C(metaclass=abc.ABCMeta): class C(metaclass=abc.ABCMeta):
@abc.abstractproperty @abc.abstractproperty
def foo(self): return 3 def foo(self): return 3
self.assertRaises(TypeError, C)
class D(C): class D(C):
@property @property
def foo(self): return super().foo def foo(self): return super().foo
self.assertEqual(D().foo, 3) self.assertEqual(D().foo, 3)
self.assertFalse(getattr(D.foo, "__isabstractmethod__", False))
def test_abstractclassmethod_basics(self): def test_abstractclassmethod_basics(self):
@abc.abstractclassmethod @abc.abstractclassmethod
...@@ -40,7 +35,7 @@ class TestABC(unittest.TestCase): ...@@ -40,7 +35,7 @@ class TestABC(unittest.TestCase):
self.assertTrue(foo.__isabstractmethod__) self.assertTrue(foo.__isabstractmethod__)
@classmethod @classmethod
def bar(cls): pass def bar(cls): pass
self.assertFalse(hasattr(bar, "__isabstractmethod__")) self.assertFalse(getattr(bar, "__isabstractmethod__", False))
class C(metaclass=abc.ABCMeta): class C(metaclass=abc.ABCMeta):
@abc.abstractclassmethod @abc.abstractclassmethod
...@@ -58,7 +53,7 @@ class TestABC(unittest.TestCase): ...@@ -58,7 +53,7 @@ class TestABC(unittest.TestCase):
self.assertTrue(foo.__isabstractmethod__) self.assertTrue(foo.__isabstractmethod__)
@staticmethod @staticmethod
def bar(): pass def bar(): pass
self.assertFalse(hasattr(bar, "__isabstractmethod__")) self.assertFalse(getattr(bar, "__isabstractmethod__", False))
class C(metaclass=abc.ABCMeta): class C(metaclass=abc.ABCMeta):
@abc.abstractstaticmethod @abc.abstractstaticmethod
...@@ -98,6 +93,163 @@ class TestABC(unittest.TestCase): ...@@ -98,6 +93,163 @@ class TestABC(unittest.TestCase):
self.assertRaises(TypeError, F) # because bar is abstract now self.assertRaises(TypeError, F) # because bar is abstract now
self.assertTrue(isabstract(F)) self.assertTrue(isabstract(F))
class TestABC(unittest.TestCase):
def test_abstractmethod_basics(self):
@abc.abstractmethod
def foo(self): pass
self.assertTrue(foo.__isabstractmethod__)
def bar(self): pass
self.assertFalse(hasattr(bar, "__isabstractmethod__"))
def test_abstractproperty_basics(self):
@property
@abc.abstractmethod
def foo(self): pass
self.assertTrue(foo.__isabstractmethod__)
def bar(self): pass
self.assertFalse(getattr(bar, "__isabstractmethod__", False))
class C(metaclass=abc.ABCMeta):
@property
@abc.abstractmethod
def foo(self): return 3
self.assertRaises(TypeError, C)
class D(C):
@C.foo.getter
def foo(self): return super().foo
self.assertEqual(D().foo, 3)
def test_abstractclassmethod_basics(self):
@classmethod
@abc.abstractmethod
def foo(cls): pass
self.assertTrue(foo.__isabstractmethod__)
@classmethod
def bar(cls): pass
self.assertFalse(getattr(bar, "__isabstractmethod__", False))
class C(metaclass=abc.ABCMeta):
@classmethod
@abc.abstractmethod
def foo(cls): return cls.__name__
self.assertRaises(TypeError, C)
class D(C):
@classmethod
def foo(cls): return super().foo()
self.assertEqual(D.foo(), 'D')
self.assertEqual(D().foo(), 'D')
def test_abstractstaticmethod_basics(self):
@staticmethod
@abc.abstractmethod
def foo(): pass
self.assertTrue(foo.__isabstractmethod__)
@staticmethod
def bar(): pass
self.assertFalse(getattr(bar, "__isabstractmethod__", False))
class C(metaclass=abc.ABCMeta):
@staticmethod
@abc.abstractmethod
def foo(): return 3
self.assertRaises(TypeError, C)
class D(C):
@staticmethod
def foo(): return 4
self.assertEqual(D.foo(), 4)
self.assertEqual(D().foo(), 4)
def test_abstractmethod_integration(self):
for abstractthing in [abc.abstractmethod, abc.abstractproperty,
abc.abstractclassmethod,
abc.abstractstaticmethod]:
class C(metaclass=abc.ABCMeta):
@abstractthing
def foo(self): pass # abstract
def bar(self): pass # concrete
self.assertEqual(C.__abstractmethods__, {"foo"})
self.assertRaises(TypeError, C) # because foo is abstract
self.assertTrue(isabstract(C))
class D(C):
def bar(self): pass # concrete override of concrete
self.assertEqual(D.__abstractmethods__, {"foo"})
self.assertRaises(TypeError, D) # because foo is still abstract
self.assertTrue(isabstract(D))
class E(D):
def foo(self): pass
self.assertEqual(E.__abstractmethods__, set())
E() # now foo is concrete, too
self.assertFalse(isabstract(E))
class F(E):
@abstractthing
def bar(self): pass # abstract override of concrete
self.assertEqual(F.__abstractmethods__, {"bar"})
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): def test_metaclass_abc(self):
# Metaclasses can be ABCs, too. # Metaclasses can be ABCs, too.
class A(metaclass=abc.ABCMeta): class A(metaclass=abc.ABCMeta):
......
...@@ -128,6 +128,29 @@ class PropertyTests(unittest.TestCase): ...@@ -128,6 +128,29 @@ class PropertyTests(unittest.TestCase):
self.assertEqual(newgetter.spam, 8) self.assertEqual(newgetter.spam, 8)
self.assertEqual(newgetter.__class__.spam.__doc__, "new docstring") 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 # Issue 5890: subclasses of property do not preserve method __doc__ strings
class PropertySub(property): class PropertySub(property):
......
...@@ -220,6 +220,7 @@ Tom Culliton ...@@ -220,6 +220,7 @@ Tom Culliton
Antonio Cuni Antonio Cuni
Brian Curtin Brian Curtin
Lisandro Dalcin Lisandro Dalcin
Darren Dale
Andrew Dalke Andrew Dalke
Lars Damerow Lars Damerow
Evan Dandrea Evan Dandrea
......
...@@ -416,6 +416,8 @@ Core and Builtins ...@@ -416,6 +416,8 @@ Core and Builtins
Library Library
------- -------
- Issue #11610: Introduce a more general way to declare abstract properties.
- Issue #13591: A bug in importlib has been fixed that caused import_module - Issue #13591: A bug in importlib has been fixed that caused import_module
to load a module twice. to load a module twice.
......
...@@ -1380,6 +1380,43 @@ property_init(PyObject *self, PyObject *args, PyObject *kwds) ...@@ -1380,6 +1380,43 @@ property_init(PyObject *self, PyObject *args, PyObject *kwds)
return 0; 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, PyDoc_STRVAR(property_doc,
"property(fget=None, fset=None, fdel=None, doc=None) -> property attribute\n" "property(fget=None, fset=None, fdel=None, doc=None) -> property attribute\n"
"\n" "\n"
...@@ -1445,7 +1482,7 @@ PyTypeObject PyProperty_Type = { ...@@ -1445,7 +1482,7 @@ PyTypeObject PyProperty_Type = {
0, /* tp_iternext */ 0, /* tp_iternext */
property_methods, /* tp_methods */ property_methods, /* tp_methods */
property_members, /* tp_members */ property_members, /* tp_members */
0, /* tp_getset */ property_getsetlist, /* tp_getset */
0, /* tp_base */ 0, /* tp_base */
0, /* tp_dict */ 0, /* tp_dict */
property_descr_get, /* tp_descr_get */ property_descr_get, /* tp_descr_get */
......
...@@ -814,6 +814,27 @@ static PyMemberDef cm_memberlist[] = { ...@@ -814,6 +814,27 @@ static PyMemberDef cm_memberlist[] = {
{NULL} /* Sentinel */ {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, PyDoc_STRVAR(classmethod_doc,
"classmethod(function) -> method\n\ "classmethod(function) -> method\n\
\n\ \n\
...@@ -865,7 +886,7 @@ PyTypeObject PyClassMethod_Type = { ...@@ -865,7 +886,7 @@ PyTypeObject PyClassMethod_Type = {
0, /* tp_iternext */ 0, /* tp_iternext */
0, /* tp_methods */ 0, /* tp_methods */
cm_memberlist, /* tp_members */ cm_memberlist, /* tp_members */
0, /* tp_getset */ cm_getsetlist, /* tp_getset */
0, /* tp_base */ 0, /* tp_base */
0, /* tp_dict */ 0, /* tp_dict */
cm_descr_get, /* tp_descr_get */ cm_descr_get, /* tp_descr_get */
...@@ -969,6 +990,27 @@ static PyMemberDef sm_memberlist[] = { ...@@ -969,6 +990,27 @@ static PyMemberDef sm_memberlist[] = {
{NULL} /* Sentinel */ {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, PyDoc_STRVAR(staticmethod_doc,
"staticmethod(function) -> method\n\ "staticmethod(function) -> method\n\
\n\ \n\
...@@ -1017,7 +1059,7 @@ PyTypeObject PyStaticMethod_Type = { ...@@ -1017,7 +1059,7 @@ PyTypeObject PyStaticMethod_Type = {
0, /* tp_iternext */ 0, /* tp_iternext */
0, /* tp_methods */ 0, /* tp_methods */
sm_memberlist, /* tp_members */ sm_memberlist, /* tp_members */
0, /* tp_getset */ sm_getsetlist, /* tp_getset */
0, /* tp_base */ 0, /* tp_base */
0, /* tp_dict */ 0, /* tp_dict */
sm_descr_get, /* tp_descr_get */ sm_descr_get, /* tp_descr_get */
......
...@@ -840,6 +840,29 @@ PyObject_SetAttrString(PyObject *v, const char *name, PyObject *w) ...@@ -840,6 +840,29 @@ PyObject_SetAttrString(PyObject *v, const char *name, PyObject *w)
return res; 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 *
_PyObject_GetAttrId(PyObject *v, _Py_Identifier *name) _PyObject_GetAttrId(PyObject *v, _Py_Identifier *name)
{ {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment