diff -r 72902ab938e0 Include/object.h --- a/Include/object.h Sun Apr 15 08:23:09 2012 +0200 +++ b/Include/object.h Mon Apr 16 02:19:52 2012 +0200 @@ -479,7 +479,7 @@ #ifndef Py_LIMITED_API PyAPI_FUNC(PyObject *) _PyType_Lookup(PyTypeObject *, PyObject *); PyAPI_FUNC(PyObject *) _PyObject_LookupSpecial(PyObject *, _Py_Identifier *); -PyAPI_FUNC(PyTypeObject *) _PyType_CalculateMetaclass(PyTypeObject *, PyObject *); +PyAPI_FUNC(PyObject *) _PyType_BuildClass(PyObject *, PyObject *, PyObject *, PyObject *); #endif PyAPI_FUNC(unsigned int) PyType_ClearCache(void); PyAPI_FUNC(void) PyType_Modified(PyTypeObject *); diff -r 72902ab938e0 Lib/test/test_operator.py --- a/Lib/test/test_operator.py Sun Apr 15 08:23:09 2012 +0200 +++ b/Lib/test/test_operator.py Mon Apr 16 02:19:52 2012 +0200 @@ -410,6 +410,36 @@ self.assertEqual(operator.__ixor__ (c, 5), "ixor") self.assertEqual(operator.__iconcat__ (c, c), "iadd") + def test_build_class(self): + def func(ns): + ns["x"] = 0 + class Meta(type): + def __init__(cls, name, bases, ns, **kw): + super().__init__(name, bases, ns) + @staticmethod + def __new__(mcls, name, bases, ns, **kw): + return super().__new__(mcls, name, bases, ns) + @classmethod + def __prepare__(mcls, name, bases, **kw): + ns = super().__prepare__(name, bases) + ns["y"] = 1 + ns.update(kw) + return ns + C = operator.build_class("C") + self.assertEqual(C.__name__, "C") + self.assertEqual(C.__bases__, (object,)) + C = operator.build_class("C", (int,)) + self.assertTrue(issubclass(C, int)) + C = operator.build_class("C", (), {"metaclass": Meta, "z": 2}) + self.assertIsInstance(C, Meta) + self.assertEqual(C.y, 1) + self.assertEqual(C.z, 2) + C = operator.build_class("C", (), {"metaclass": Meta, "z": 2}, func) + self.assertIsInstance(C, Meta) + self.assertEqual(C.x, 0) + self.assertEqual(C.y, 1) + self.assertEqual(C.z, 2) + def test_main(verbose=None): import sys test_classes = ( diff -r 72902ab938e0 Modules/operator.c --- a/Modules/operator.c Sun Apr 15 08:23:09 2012 +0200 +++ b/Modules/operator.c Mon Apr 16 02:19:52 2012 +0200 @@ -155,6 +155,50 @@ return result; } +static PyObject* +build_class(PyObject *s, PyObject *args, PyObject *kw) +{ + static char *kwlist[] = {"name", "bases", "kwds", "eval_body", 0}; + PyObject *name; + PyObject *bases = NULL; + PyObject *kwds = NULL; + PyObject *eval_body = NULL; + int free_bases = 0; + PyObject *cls; + + if (!PyArg_ParseTupleAndKeywords(args, kw, "U|O!O!O:build_class", kwlist, + &name, + &PyTuple_Type, &bases, + &PyDict_Type, &kwds, + &eval_body)) { + return NULL; + } + + if ((eval_body != NULL) && !PyCallable_Check(eval_body)) { + PyErr_SetString(PyExc_TypeError, "build_class: eval_body is not a callable"); + return NULL; + } + + if (bases == NULL) { + bases = PyTuple_New(0); + if (bases == NULL) { + return NULL; + } + free_bases = 1; + } + + cls = _PyType_BuildClass(eval_body, name, bases, kwds); + if (free_bases) { + Py_DECREF(bases); + } + return cls; +} + +PyDoc_STRVAR(build_class_doc, + "build_class(name, bases=(), kwds=None, eval_body=None) -> cls\n\ + \n\ + Dynamically create a class object."); + #undef spam1 #undef spam2 #undef spam1o @@ -227,6 +271,8 @@ spam2(gt,__gt__, "gt(a, b) -- Same as a>b.") spam2(ge,__ge__, "ge(a, b) -- Same as a>=b.") +{"build_class", (PyCFunction)build_class, METH_VARARGS | METH_KEYWORDS, build_class_doc}, + {NULL, NULL} /* sentinel */ }; diff -r 72902ab938e0 Objects/typeobject.c --- a/Objects/typeobject.c Sun Apr 15 08:23:09 2012 +0200 +++ b/Objects/typeobject.c Mon Apr 16 02:19:52 2012 +0200 @@ -1926,8 +1926,8 @@ } /* Determine the most derived metatype. */ -PyTypeObject * -_PyType_CalculateMetaclass(PyTypeObject *metatype, PyObject *bases) +static PyTypeObject * +calculate_metaclass(PyTypeObject *metatype, PyObject *bases) { Py_ssize_t i, nbases; PyTypeObject *winner; @@ -1961,6 +1961,147 @@ return winner; } +/* Call the __prepare__ of the correct metaclass. + 'name' will be passed directly to __prepare__. + 'meta' is a pointer to the PyObject * which is the initial metaclass. + It will be ovewritten with the calculated most derived metaclass. + 'bases' must be a tuple. + 'mkw' must be a dict or NULL. +*/ +static PyObject * +prepare_namespace(PyObject *name, PyObject **meta, PyObject *bases, PyObject *mkw) +{ + PyObject *winner, *prep, *ns; + _Py_IDENTIFIER(__prepare__); + + if (PyType_Check(*meta)) { + /* meta is really a class, so check for a more derived + metaclass, or possible metaclass conflicts: */ + winner = (PyObject *)calculate_metaclass((PyTypeObject *)(*meta), + bases); + if (winner == NULL) { + return NULL; + } + } + else { + /* meta is not a class, so we cannot do the metaclass + calculation, so we will use the explicitly given object as it is: */ + winner = *meta; + } + + /* Call __prepare__: */ + prep = _PyObject_GetAttrId(winner, &PyId___prepare__); + if (prep == NULL) { + if (PyErr_ExceptionMatches(PyExc_AttributeError)) { + PyErr_Clear(); + ns = PyDict_New(); + } + else { + return NULL; + } + } + else { + PyObject *pargs = PyTuple_Pack(2, name, bases); + if (pargs == NULL) { + Py_DECREF(prep); + return NULL; + } + ns = PyEval_CallObjectWithKeywords(prep, pargs, mkw); + Py_DECREF(pargs); + Py_DECREF(prep); + } + + /* Pass back the winner to the caller: */ + if ((*meta) != winner) { + Py_DECREF(*meta); + Py_INCREF(winner); + *meta = winner; + } + + return ns; +} + +/* Create a class object dynamically. + 'func' is a callable (or NULL) which expects + the namespace returned by __prepare__. + 'name' must be a str object. + 'bases' must be a tuple. + 'kwds' must be a dict or NULL. */ +PyObject * +_PyType_BuildClass(PyObject *func, PyObject *name, PyObject *bases, PyObject *kwds) +{ + PyObject *meta, *mkw, *ns, *cell, *cls = NULL; + + /* Get the metaclass: */ + if (kwds == NULL) { + meta = NULL; + mkw = NULL; + } + else { + mkw = PyDict_Copy(kwds); /* Don't modify kwds passed in! */ + if (mkw == NULL) { + return NULL; + } + meta = PyDict_GetItemString(mkw, "metaclass"); + if (meta != NULL) { + Py_INCREF(meta); + if (PyDict_DelItemString(mkw, "metaclass") < 0) { + Py_DECREF(meta); + Py_DECREF(mkw); + return NULL; + } + } + } + if (meta == NULL) { + /* if there are no bases, use type: */ + if (PyTuple_GET_SIZE(bases) == 0) { + meta = (PyObject *) (&PyType_Type); + } + /* else get the type of the first base */ + else { + PyObject *base0 = PyTuple_GET_ITEM(bases, 0); + meta = (PyObject *) (base0->ob_type); + } + Py_INCREF(meta); + } + + /* Call __prepare__ (and get the most derived metaclass): */ + ns = prepare_namespace(name, &meta, bases, mkw); + if (ns == NULL) { + Py_DECREF(meta); + Py_XDECREF(mkw); + return NULL; + } + + /* Call the callable with the namespace: */ + if (func != NULL) { + cell = PyObject_CallFunctionObjArgs(func, ns, NULL); + } + else { + cell = Py_None; + Py_INCREF(cell); + } + + /* Call the correct metaclass to create the class object: */ + if (cell != NULL) { + PyObject *margs; + margs = PyTuple_Pack(3, name, bases, ns); + if (margs != NULL) { + cls = PyEval_CallObjectWithKeywords(meta, margs, mkw); + Py_DECREF(margs); + } + if (cls != NULL && PyCell_Check(cell)) { + Py_INCREF(cls); + PyCell_Set(cell, cls); + } + Py_DECREF(cell); + } + Py_DECREF(ns); + Py_DECREF(meta); + Py_XDECREF(mkw); + return cls; +} + static PyObject * type_new(PyTypeObject *metatype, PyObject *args, PyObject *kwds) { @@ -2007,7 +2148,7 @@ return NULL; /* Determine the proper metatype to deal with this: */ - winner = _PyType_CalculateMetaclass(metatype, bases); + winner = calculate_metaclass(metatype, bases); if (winner == NULL) { return NULL; } diff -r 72902ab938e0 Python/bltinmodule.c --- a/Python/bltinmodule.c Sun Apr 15 08:23:09 2012 +0200 +++ b/Python/bltinmodule.c Mon Apr 16 02:19:52 2012 +0200 @@ -38,11 +38,9 @@ static PyObject * builtin___build_class__(PyObject *self, PyObject *args, PyObject *kwds) { - PyObject *func, *name, *bases, *mkw, *meta, *winner, *prep, *ns, *cell; + PyObject *func, *name, *bases; PyObject *cls = NULL; Py_ssize_t nargs; - int isclass; - _Py_IDENTIFIER(__prepare__); assert(args != NULL); if (!PyTuple_Check(args)) { @@ -67,111 +65,7 @@ if (bases == NULL) return NULL; - if (kwds == NULL) { - meta = NULL; - mkw = NULL; - } - else { - mkw = PyDict_Copy(kwds); /* Don't modify kwds passed in! */ - if (mkw == NULL) { - Py_DECREF(bases); - return NULL; - } - meta = PyDict_GetItemString(mkw, "metaclass"); - if (meta != NULL) { - Py_INCREF(meta); - if (PyDict_DelItemString(mkw, "metaclass") < 0) { - Py_DECREF(meta); - Py_DECREF(mkw); - Py_DECREF(bases); - return NULL; - } - /* metaclass is explicitly given, check if it's indeed a class */ - isclass = PyType_Check(meta); - } - } - if (meta == NULL) { - /* if there are no bases, use type: */ - if (PyTuple_GET_SIZE(bases) == 0) { - meta = (PyObject *) (&PyType_Type); - } - /* else get the type of the first base */ - else { - PyObject *base0 = PyTuple_GET_ITEM(bases, 0); - meta = (PyObject *) (base0->ob_type); - } - Py_INCREF(meta); - isclass = 1; /* meta is really a class */ - } - - if (isclass) { - /* meta is really a class, so check for a more derived - metaclass, or possible metaclass conflicts: */ - winner = (PyObject *)_PyType_CalculateMetaclass((PyTypeObject *)meta, - bases); - if (winner == NULL) { - Py_DECREF(meta); - Py_XDECREF(mkw); - Py_DECREF(bases); - return NULL; - } - if (winner != meta) { - Py_DECREF(meta); - meta = winner; - Py_INCREF(meta); - } - } - /* else: meta is not a class, so we cannot do the metaclass - calculation, so we will use the explicitly given object as it is */ - prep = _PyObject_GetAttrId(meta, &PyId___prepare__); - if (prep == NULL) { - if (PyErr_ExceptionMatches(PyExc_AttributeError)) { - PyErr_Clear(); - ns = PyDict_New(); - } - else { - Py_DECREF(meta); - Py_XDECREF(mkw); - Py_DECREF(bases); - return NULL; - } - } - else { - PyObject *pargs = PyTuple_Pack(2, name, bases); - if (pargs == NULL) { - Py_DECREF(prep); - Py_DECREF(meta); - Py_XDECREF(mkw); - Py_DECREF(bases); - return NULL; - } - ns = PyEval_CallObjectWithKeywords(prep, pargs, mkw); - Py_DECREF(pargs); - Py_DECREF(prep); - } - if (ns == NULL) { - Py_DECREF(meta); - Py_XDECREF(mkw); - Py_DECREF(bases); - return NULL; - } - cell = PyObject_CallFunctionObjArgs(func, ns, NULL); - if (cell != NULL) { - PyObject *margs; - margs = PyTuple_Pack(3, name, bases, ns); - if (margs != NULL) { - cls = PyEval_CallObjectWithKeywords(meta, margs, mkw); - Py_DECREF(margs); - } - if (cls != NULL && PyCell_Check(cell)) { - Py_INCREF(cls); - PyCell_SET(cell, cls); - } - Py_DECREF(cell); - } - Py_DECREF(ns); - Py_DECREF(meta); - Py_XDECREF(mkw); + cls = _PyType_BuildClass(func, name, bases, kwds); Py_DECREF(bases); return cls; }