bpo-32226: Implementation of PEP 560 (core components) (#4732)

This part of the PEP implementation adds support for
__mro_entries__ and __class_getitem__ by updating
__build_class__ and PyObject_GetItem.
This commit is contained in:
Ivan Levkivskyi 2017-12-14 23:32:56 +01:00 committed by GitHub
parent 15a8728415
commit 2b5fd1e9ca
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 492 additions and 5 deletions

View file

@ -37,6 +37,7 @@ _Py_IDENTIFIER(__builtins__);
_Py_IDENTIFIER(__dict__);
_Py_IDENTIFIER(__prepare__);
_Py_IDENTIFIER(__round__);
_Py_IDENTIFIER(__mro_entries__);
_Py_IDENTIFIER(encoding);
_Py_IDENTIFIER(errors);
_Py_IDENTIFIER(fileno);
@ -49,12 +50,86 @@ _Py_IDENTIFIER(stderr);
#include "clinic/bltinmodule.c.h"
static PyObject*
update_bases(PyObject *bases, PyObject *const *args, int nargs)
{
int i, j;
PyObject *base, *meth, *new_base, *result, *new_bases = NULL;
PyObject *stack[1] = {bases};
assert(PyTuple_Check(bases));
for (i = 0; i < nargs; i++) {
base = args[i];
if (PyType_Check(base)) {
if (new_bases) {
/* If we already have made a replacement, then we append every normal base,
otherwise just skip it. */
if (PyList_Append(new_bases, base) < 0) {
goto error;
}
}
continue;
}
meth = _PyObject_GetAttrId(base, &PyId___mro_entries__);
if (!meth) {
if (!PyErr_ExceptionMatches(PyExc_AttributeError)) {
goto error;
}
PyErr_Clear();
if (new_bases) {
if (PyList_Append(new_bases, base) < 0) {
goto error;
}
}
continue;
}
new_base = _PyObject_FastCall(meth, stack, 1);
Py_DECREF(meth);
if (!new_base) {
goto error;
}
if (!PyTuple_Check(new_base)) {
PyErr_SetString(PyExc_TypeError,
"__mro_entries__ must return a tuple");
Py_DECREF(new_base);
goto error;
}
if (!new_bases) {
/* If this is a first successful replacement, create new_bases list and
copy previously encountered bases. */
if (!(new_bases = PyList_New(i))) {
goto error;
}
for (j = 0; j < i; j++) {
base = args[j];
PyList_SET_ITEM(new_bases, j, base);
Py_INCREF(base);
}
}
j = PyList_GET_SIZE(new_bases);
if (PyList_SetSlice(new_bases, j, j, new_base) < 0) {
goto error;
}
Py_DECREF(new_base);
}
if (!new_bases) {
return bases;
}
result = PyList_AsTuple(new_bases);
Py_DECREF(new_bases);
return result;
error:
Py_XDECREF(new_bases);
return NULL;
}
/* AC: cannot convert yet, waiting for *args support */
static PyObject *
builtin___build_class__(PyObject *self, PyObject **args, Py_ssize_t nargs,
PyObject *kwnames)
{
PyObject *func, *name, *bases, *mkw, *meta, *winner, *prep, *ns;
PyObject *func, *name, *bases, *mkw, *meta, *winner, *prep, *ns, *orig_bases;
PyObject *cls = NULL, *cell = NULL;
int isclass = 0; /* initialize to prevent gcc warning */
@ -75,10 +150,16 @@ builtin___build_class__(PyObject *self, PyObject **args, Py_ssize_t nargs,
"__build_class__: name is not a string");
return NULL;
}
bases = _PyStack_AsTupleSlice(args, nargs, 2, nargs);
if (bases == NULL)
orig_bases = _PyStack_AsTupleSlice(args, nargs, 2, nargs);
if (orig_bases == NULL)
return NULL;
bases = update_bases(orig_bases, args + 2, nargs - 2);
if (bases == NULL) {
Py_DECREF(orig_bases);
return NULL;
}
if (kwnames == NULL) {
meta = NULL;
mkw = NULL;
@ -171,6 +252,11 @@ builtin___build_class__(PyObject *self, PyObject **args, Py_ssize_t nargs,
NULL, 0, NULL, 0, NULL, 0, NULL,
PyFunction_GET_CLOSURE(func));
if (cell != NULL) {
if (bases != orig_bases) {
if (PyMapping_SetItemString(ns, "__orig_bases__", orig_bases) < 0) {
goto error;
}
}
PyObject *margs[3] = {name, bases, ns};
cls = _PyObject_FastCallDict(meta, margs, 3, mkw);
if (cls != NULL && PyType_Check(cls) && PyCell_Check(cell)) {
@ -209,6 +295,9 @@ error:
Py_DECREF(meta);
Py_XDECREF(mkw);
Py_DECREF(bases);
if (bases != orig_bases) {
Py_DECREF(orig_bases);
}
return cls;
}