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.
This commit is contained in:
Jeffrey Yasskin 2008-02-28 04:45:36 +00:00
parent c105289ec4
commit 960b9b7a2f
4 changed files with 108 additions and 48 deletions

View file

@ -305,6 +305,40 @@ type_set_module(PyTypeObject *type, PyObject *value, void *context)
return PyDict_SetItemString(type->tp_dict, "__module__", value);
}
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)
{
@ -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}