]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
Extend the "Don Beaudry hack" with "Guido's corollary" -- if the base
authorGuido van Rossum <guido@python.org>
Thu, 31 Jul 1997 03:54:02 +0000 (03:54 +0000)
committerGuido van Rossum <guido@python.org>
Thu, 31 Jul 1997 03:54:02 +0000 (03:54 +0000)
class has a __class__ attribute, call that to create the new class.
This allows us to write metaclasses purely in C!

Python/ceval.c

index ec2e419e24fe00c917f900e083a59581de8c9614..205d8d42e069e1631b325a1fc5c8d1d5e52d94a3 100644 (file)
@@ -2689,22 +2689,39 @@ build_class(methods, bases, name)
                return NULL;
        }
        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);
                if (!PyClass_Check(base)) {
                        /* Call the base's *type*, if it is callable.
                           This code is a hook for Donald Beaudry's
                           and Jim Fulton's type extensions.  In
                           unexended Python it will never be triggered
-                          since its types are not callable. */
-                       if (base->ob_type->ob_type->tp_call) {
-                               PyObject *args;
-                               PyObject *class;
-                               args = Py_BuildValue("(OOO)",
-                                                    name, bases, methods);
-                               class = PyEval_CallObject(
-                                       (PyObject *)base->ob_type, args);
-                               Py_DECREF(args);
-                               return class;
+                          since its types are not callable.
+                          Ditto: call the bases's *class*, if it has
+                          one.  This makes the same thing possible
+                          without writing C code.  A true meta-object
+                          protocol! */
+                       PyObject *basetype = (PyObject *)base->ob_type;
+                       PyObject *callable = NULL;
+                       if (PyCallable_Check(basetype))
+                               callable = basetype;
+                       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,
                                "base is not a class object");