summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJeffrey Yasskin <jyasskin@gmail.com>2008-02-28 04:45:36 (GMT)
committerJeffrey Yasskin <jyasskin@gmail.com>2008-02-28 04:45:36 (GMT)
commit960b9b7a2f6e9b4f0e6b692d604c53b3fd2f5d1c (patch)
tree9f0cc6bac83e4c2607c6bea64249932fc5353859
parentc105289ec4e22f5d53f3e5872ad6a49da8a33b6d (diff)
downloadcpython-960b9b7a2f6e9b4f0e6b692d604c53b3fd2f5d1c.zip
cpython-960b9b7a2f6e9b4f0e6b692d604c53b3fd2f5d1c.tar.gz
cpython-960b9b7a2f6e9b4f0e6b692d604c53b3fd2f5d1c.tar.bz2
Move abc._Abstract into object by adding a new flag Py_TPFLAGS_IS_ABSTRACT,
which forbids constructing types that have it set. The effect is to speed ./python.exe -m timeit -s 'import abc' -s 'class Foo(object): __metaclass__ = abc.ABCMeta' 'Foo()' up from 2.5us to 0.201us. This fixes issue 1762.
-rw-r--r--Include/object.h3
-rw-r--r--Lib/abc.py49
-rw-r--r--Lib/test/test_descrtut.py1
-rw-r--r--Objects/typeobject.c103
4 files changed, 108 insertions, 48 deletions
diff --git a/Include/object.h b/Include/object.h
index 65440a6..8d04935 100644
--- a/Include/object.h
+++ b/Include/object.h
@@ -537,6 +537,9 @@ given type object has a specified feature.
#define Py_TPFLAGS_HAVE_VERSION_TAG (1L<<18)
#define Py_TPFLAGS_VALID_VERSION_TAG (1L<<19)
+/* Type is abstract and cannot be instantiated */
+#define Py_TPFLAGS_IS_ABSTRACT (1L<<20)
+
/* These flags are used to determine if a type is a subclass. */
#define Py_TPFLAGS_INT_SUBCLASS (1L<<23)
#define Py_TPFLAGS_LONG_SUBCLASS (1L<<24)
diff --git a/Lib/abc.py b/Lib/abc.py
index 9d15012..5da2590 100644
--- a/Lib/abc.py
+++ b/Lib/abc.py
@@ -51,52 +51,6 @@ class abstractproperty(property):
__isabstractmethod__ = True
-class _Abstract(object):
-
- """Helper class inserted into the bases by ABCMeta (using _fix_bases()).
-
- You should never need to explicitly subclass this class.
-
- There should never be a base class between _Abstract and object.
- """
-
- def __new__(cls, *args, **kwds):
- am = cls.__dict__.get("__abstractmethods__")
- if am:
- raise TypeError("Can't instantiate abstract class %s "
- "with abstract methods %s" %
- (cls.__name__, ", ".join(sorted(am))))
- if (args or kwds) and cls.__init__ is object.__init__:
- raise TypeError("Can't pass arguments to __new__ "
- "without overriding __init__")
- return super(_Abstract, cls).__new__(cls)
-
- @classmethod
- def __subclasshook__(cls, subclass):
- """Abstract classes can override this to customize issubclass().
-
- This is invoked early on by __subclasscheck__() below. It
- should return True, False or NotImplemented. If it returns
- NotImplemented, the normal algorithm is used. Otherwise, it
- overrides the normal algorithm (and the outcome is cached).
- """
- return NotImplemented
-
-
-def _fix_bases(bases):
- """Helper method that inserts _Abstract in the bases if needed."""
- for base in bases:
- if issubclass(base, _Abstract):
- # _Abstract is already a base (maybe indirectly)
- return bases
- if object in bases:
- # Replace object with _Abstract
- return tuple([_Abstract if base is object else base
- for base in bases])
- # Append _Abstract to the end
- return bases + (_Abstract,)
-
-
class ABCMeta(type):
"""Metaclass for defining Abstract Base Classes (ABCs).
@@ -119,7 +73,6 @@ class ABCMeta(type):
_abc_invalidation_counter = 0
def __new__(mcls, name, bases, namespace):
- bases = _fix_bases(bases)
cls = super(ABCMeta, mcls).__new__(mcls, name, bases, namespace)
# Compute set of abstract method names
abstracts = set(name
@@ -130,7 +83,7 @@ class ABCMeta(type):
value = getattr(cls, name, None)
if getattr(value, "__isabstractmethod__", False):
abstracts.add(name)
- cls.__abstractmethods__ = abstracts
+ cls.__abstractmethods__ = frozenset(abstracts)
# Set up inheritance registry
cls._abc_registry = set()
cls._abc_cache = set()
diff --git a/Lib/test/test_descrtut.py b/Lib/test/test_descrtut.py
index 94e9845..514e398 100644
--- a/Lib/test/test_descrtut.py
+++ b/Lib/test/test_descrtut.py
@@ -209,6 +209,7 @@ Instead, you can get the same information from the list type:
'__setitem__',
'__setslice__',
'__str__',
+ '__subclasshook__',
'append',
'count',
'extend',
diff --git a/Objects/typeobject.c b/Objects/typeobject.c
index 07ab61f..7db6dac 100644
--- a/Objects/typeobject.c
+++ b/Objects/typeobject.c
@@ -306,6 +306,40 @@ type_set_module(PyTypeObject *type, PyObject *value, void *context)
}
static PyObject *
+type_abstractmethods(PyTypeObject *type, void *context)
+{
+ PyObject *mod = PyDict_GetItemString(type->tp_dict,
+ "__abstractmethods__");
+ if (!mod) {
+ PyErr_Format(PyExc_AttributeError, "__abstractmethods__");
+ return NULL;
+ }
+ Py_XINCREF(mod);
+ return mod;
+}
+
+static int
+type_set_abstractmethods(PyTypeObject *type, PyObject *value, void *context)
+{
+ /* __abstractmethods__ should only be set once on a type, in
+ abc.ABCMeta.__new__, so this function doesn't do anything
+ special to update subclasses.
+ */
+ int res = PyDict_SetItemString(type->tp_dict,
+ "__abstractmethods__", value);
+ if (res == 0) {
+ type_modified(type);
+ if (value && PyObject_IsTrue(value)) {
+ type->tp_flags |= Py_TPFLAGS_IS_ABSTRACT;
+ }
+ else {
+ type->tp_flags &= ~Py_TPFLAGS_IS_ABSTRACT;
+ }
+ }
+ return res;
+}
+
+static PyObject *
type_get_bases(PyTypeObject *type, void *context)
{
Py_INCREF(type->tp_bases);
@@ -542,6 +576,8 @@ static PyGetSetDef type_getsets[] = {
{"__name__", (getter)type_name, (setter)type_set_name, NULL},
{"__bases__", (getter)type_get_bases, (setter)type_set_bases, NULL},
{"__module__", (getter)type_module, (setter)type_set_module, NULL},
+ {"__abstractmethods__", (getter)type_abstractmethods,
+ (setter)type_set_abstractmethods, NULL},
{"__dict__", (getter)type_dict, NULL, NULL},
{"__doc__", (getter)type_get_doc, NULL, NULL},
{0}
@@ -2749,6 +2785,56 @@ object_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
}
if (err < 0)
return NULL;
+
+ if (type->tp_flags & Py_TPFLAGS_IS_ABSTRACT) {
+ static PyObject *comma = NULL;
+ PyObject *abstract_methods = NULL;
+ PyObject *builtins;
+ PyObject *sorted;
+ PyObject *sorted_methods = NULL;
+ PyObject *joined = NULL;
+ const char *joined_str;
+
+ /* Compute ", ".join(sorted(type.__abstractmethods__))
+ into joined. */
+ abstract_methods = type_abstractmethods(type, NULL);
+ if (abstract_methods == NULL)
+ goto error;
+ builtins = PyEval_GetBuiltins();
+ if (builtins == NULL)
+ goto error;
+ sorted = PyDict_GetItemString(builtins, "sorted");
+ if (sorted == NULL)
+ goto error;
+ sorted_methods = PyObject_CallFunctionObjArgs(sorted,
+ abstract_methods,
+ NULL);
+ if (sorted_methods == NULL)
+ goto error;
+ if (comma == NULL) {
+ comma = PyString_InternFromString(", ");
+ if (comma == NULL)
+ goto error;
+ }
+ joined = PyObject_CallMethod(comma, "join",
+ "O", sorted_methods);
+ if (joined == NULL)
+ goto error;
+ joined_str = PyString_AsString(joined);
+ if (joined_str == NULL)
+ goto error;
+
+ PyErr_Format(PyExc_TypeError,
+ "Can't instantiate abstract class %s "
+ "with abstract methods %s",
+ type->tp_name,
+ joined_str);
+ error:
+ Py_XDECREF(joined);
+ Py_XDECREF(sorted_methods);
+ Py_XDECREF(abstract_methods);
+ return NULL;
+ }
return type->tp_alloc(type, 0);
}
@@ -3210,6 +3296,21 @@ object_reduce_ex(PyObject *self, PyObject *args)
return _common_reduce(self, proto);
}
+static PyObject *
+object_subclasshook(PyObject *cls, PyObject *args)
+{
+ Py_INCREF(Py_NotImplemented);
+ return Py_NotImplemented;
+}
+
+PyDoc_STRVAR(object_subclasshook_doc,
+"Abstract classes can override this to customize issubclass().\n"
+"\n"
+"This is invoked early on by abc.ABCMeta.__subclasscheck__().\n"
+"It should return True, False or NotImplemented. If it returns\n"
+"NotImplemented, the normal algorithm is used. Otherwise, it\n"
+"overrides the normal algorithm (and the outcome is cached).\n");
+
/*
from PEP 3101, this code implements:
@@ -3259,6 +3360,8 @@ static PyMethodDef object_methods[] = {
PyDoc_STR("helper for pickle")},
{"__reduce__", object_reduce, METH_VARARGS,
PyDoc_STR("helper for pickle")},
+ {"__subclasshook__", object_subclasshook, METH_CLASS | METH_VARARGS,
+ object_subclasshook_doc},
{"__format__", object_format, METH_VARARGS,
PyDoc_STR("default object formatter")},
{0}