Extend the "Don Beaudry hack" with "Guido's corollary" -- if the base

class has a __class__ attribute, call that to create the new class.
This allows us to write metaclasses purely in C!
This commit is contained in:
Guido van Rossum 1997-07-31 03:54:02 +00:00
parent fd16ca4542
commit 55b9ab5bdb

View file

@ -2689,22 +2689,39 @@ build_class(methods, bases, name)
return NULL; return NULL;
} }
for (i = PyTuple_Size(bases); --i >= 0; ) { for (i = PyTuple_Size(bases); --i >= 0; ) {
/* XXX Is it intentional that the *last* base gets a
chance at this first? */
PyObject *base = PyTuple_GET_ITEM(bases, i); PyObject *base = PyTuple_GET_ITEM(bases, i);
if (!PyClass_Check(base)) { if (!PyClass_Check(base)) {
/* Call the base's *type*, if it is callable. /* Call the base's *type*, if it is callable.
This code is a hook for Donald Beaudry's This code is a hook for Donald Beaudry's
and Jim Fulton's type extensions. In and Jim Fulton's type extensions. In
unexended Python it will never be triggered unexended Python it will never be triggered
since its types are not callable. */ since its types are not callable.
if (base->ob_type->ob_type->tp_call) { Ditto: call the bases's *class*, if it has
PyObject *args; one. This makes the same thing possible
PyObject *class; without writing C code. A true meta-object
args = Py_BuildValue("(OOO)", protocol! */
name, bases, methods); PyObject *basetype = (PyObject *)base->ob_type;
class = PyEval_CallObject( PyObject *callable = NULL;
(PyObject *)base->ob_type, args); if (PyCallable_Check(basetype))
Py_DECREF(args); callable = basetype;
return class; else
callable = PyObject_GetAttrString(
base, "__class__");
if (callable) {
PyObject *args;
PyObject *newclass = NULL;
args = Py_BuildValue(
"(OOO)", name, bases, methods);
if (args != NULL) {
newclass = PyEval_CallObject(
callable, args);
Py_DECREF(args);
}
if (callable != basetype)
Py_DECREF(callable);
return newclass;
} }
PyErr_SetString(PyExc_TypeError, PyErr_SetString(PyExc_TypeError,
"base is not a class object"); "base is not a class object");