mirror of https://github.com/python/cpython
bpo-45383: Get metaclass from bases in PyType_From* (GH-28748)
This checks the bases of of a type created using the FromSpec API to inherit the bases metaclasses. The metaclass's alloc function will be called as is done in `tp_new` for classes created in Python. Co-authored-by: Petr Viktorin <encukou@gmail.com> Co-authored-by: Erlend Egeberg Aasland <erlend.aasland@protonmail.com>
This commit is contained in:
parent
a5ba0f4ebc
commit
7fef847662
|
@ -193,11 +193,12 @@ The following functions and structs are used to create
|
||||||
.. c:function:: PyObject* PyType_FromMetaclass(PyTypeObject *metaclass, PyObject *module, PyType_Spec *spec, PyObject *bases)
|
.. c:function:: PyObject* PyType_FromMetaclass(PyTypeObject *metaclass, PyObject *module, PyType_Spec *spec, PyObject *bases)
|
||||||
|
|
||||||
Create and return a :ref:`heap type <heap-types>` from the *spec*
|
Create and return a :ref:`heap type <heap-types>` from the *spec*
|
||||||
(:const:`Py_TPFLAGS_HEAPTYPE`).
|
(see :const:`Py_TPFLAGS_HEAPTYPE`).
|
||||||
|
|
||||||
The metaclass *metaclass* is used to construct the resulting type object.
|
The metaclass *metaclass* is used to construct the resulting type object.
|
||||||
When *metaclass* is ``NULL``, the default :c:type:`PyType_Type` is used
|
When *metaclass* is ``NULL``, the metaclass is derived from *bases*
|
||||||
instead. Note that metaclasses that override
|
(or *Py_tp_base[s]* slots if *bases* is ``NULL``, see below).
|
||||||
|
Note that metaclasses that override
|
||||||
:c:member:`~PyTypeObject.tp_new` are not supported.
|
:c:member:`~PyTypeObject.tp_new` are not supported.
|
||||||
|
|
||||||
The *bases* argument can be used to specify base classes; it can either
|
The *bases* argument can be used to specify base classes; it can either
|
||||||
|
@ -215,6 +216,19 @@ The following functions and structs are used to create
|
||||||
|
|
||||||
This function calls :c:func:`PyType_Ready` on the new type.
|
This function calls :c:func:`PyType_Ready` on the new type.
|
||||||
|
|
||||||
|
Note that this function does *not* fully match the behavior of
|
||||||
|
calling :py:class:`type() <type>` or using the :keyword:`class` statement.
|
||||||
|
With user-provided base types or metaclasses, prefer
|
||||||
|
:ref:`calling <capi-call>` :py:class:`type` (or the metaclass)
|
||||||
|
over ``PyType_From*`` functions.
|
||||||
|
Specifically:
|
||||||
|
|
||||||
|
* :py:meth:`~object.__new__` is not called on the new class
|
||||||
|
(and it must be set to ``type.__new__``).
|
||||||
|
* :py:meth:`~object.__init__` is not called on the new class.
|
||||||
|
* :py:meth:`~object.__init_subclass__` is not called on any bases.
|
||||||
|
* :py:meth:`~object.__set_name__` is not called on new descriptors.
|
||||||
|
|
||||||
.. versionadded:: 3.12
|
.. versionadded:: 3.12
|
||||||
|
|
||||||
.. c:function:: PyObject* PyType_FromModuleAndSpec(PyObject *module, PyType_Spec *spec, PyObject *bases)
|
.. c:function:: PyObject* PyType_FromModuleAndSpec(PyObject *module, PyType_Spec *spec, PyObject *bases)
|
||||||
|
@ -228,6 +242,11 @@ The following functions and structs are used to create
|
||||||
The function now accepts a single class as the *bases* argument and
|
The function now accepts a single class as the *bases* argument and
|
||||||
``NULL`` as the ``tp_doc`` slot.
|
``NULL`` as the ``tp_doc`` slot.
|
||||||
|
|
||||||
|
.. versionchanged:: 3.12
|
||||||
|
|
||||||
|
The function now finds and uses a metaclass corresponding to the provided
|
||||||
|
base classes. Previously, only :class:`type` instances were returned.
|
||||||
|
|
||||||
|
|
||||||
.. c:function:: PyObject* PyType_FromSpecWithBases(PyType_Spec *spec, PyObject *bases)
|
.. c:function:: PyObject* PyType_FromSpecWithBases(PyType_Spec *spec, PyObject *bases)
|
||||||
|
|
||||||
|
@ -235,10 +254,21 @@ The following functions and structs are used to create
|
||||||
|
|
||||||
.. versionadded:: 3.3
|
.. versionadded:: 3.3
|
||||||
|
|
||||||
|
.. versionchanged:: 3.12
|
||||||
|
|
||||||
|
The function now finds and uses a metaclass corresponding to the provided
|
||||||
|
base classes. Previously, only :class:`type` instances were returned.
|
||||||
|
|
||||||
.. c:function:: PyObject* PyType_FromSpec(PyType_Spec *spec)
|
.. c:function:: PyObject* PyType_FromSpec(PyType_Spec *spec)
|
||||||
|
|
||||||
Equivalent to ``PyType_FromMetaclass(NULL, NULL, spec, NULL)``.
|
Equivalent to ``PyType_FromMetaclass(NULL, NULL, spec, NULL)``.
|
||||||
|
|
||||||
|
.. versionchanged:: 3.12
|
||||||
|
|
||||||
|
The function now finds and uses a metaclass corresponding to the
|
||||||
|
base classes provided in *Py_tp_base[s]* slots.
|
||||||
|
Previously, only :class:`type` instances were returned.
|
||||||
|
|
||||||
.. c:type:: PyType_Spec
|
.. c:type:: PyType_Spec
|
||||||
|
|
||||||
Structure defining a type's behavior.
|
Structure defining a type's behavior.
|
||||||
|
|
|
@ -0,0 +1,3 @@
|
||||||
|
The :c:func:`PyType_FromSpec` API will now find and use a metaclass
|
||||||
|
based on the provided bases.
|
||||||
|
An error will be raised if there is a metaclass conflict.
|
|
@ -1208,6 +1208,161 @@ test_get_type_name(PyObject *self, PyObject *Py_UNUSED(ignored))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static PyType_Slot empty_type_slots[] = {
|
||||||
|
{0, 0},
|
||||||
|
};
|
||||||
|
|
||||||
|
static PyType_Spec MinimalMetaclass_spec = {
|
||||||
|
.name = "_testcapi.MinimalMetaclass",
|
||||||
|
.basicsize = sizeof(PyHeapTypeObject),
|
||||||
|
.flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
|
||||||
|
.slots = empty_type_slots,
|
||||||
|
};
|
||||||
|
|
||||||
|
static PyType_Spec MinimalType_spec = {
|
||||||
|
.name = "_testcapi.MinimalSpecType",
|
||||||
|
.basicsize = sizeof(PyObject),
|
||||||
|
.flags = Py_TPFLAGS_DEFAULT,
|
||||||
|
.slots = empty_type_slots,
|
||||||
|
};
|
||||||
|
|
||||||
|
static PyObject *
|
||||||
|
test_from_spec_metatype_inheritance(PyObject *self, PyObject *Py_UNUSED(ignored))
|
||||||
|
{
|
||||||
|
PyObject *metaclass = NULL;
|
||||||
|
PyObject *class = NULL;
|
||||||
|
PyObject *new = NULL;
|
||||||
|
PyObject *subclasses = NULL;
|
||||||
|
PyObject *result = NULL;
|
||||||
|
int r;
|
||||||
|
|
||||||
|
metaclass = PyType_FromSpecWithBases(&MinimalMetaclass_spec, (PyObject*)&PyType_Type);
|
||||||
|
if (metaclass == NULL) {
|
||||||
|
goto finally;
|
||||||
|
}
|
||||||
|
class = PyObject_CallFunction(metaclass, "s(){}", "TestClass");
|
||||||
|
if (class == NULL) {
|
||||||
|
goto finally;
|
||||||
|
}
|
||||||
|
|
||||||
|
new = PyType_FromSpecWithBases(&MinimalType_spec, class);
|
||||||
|
if (new == NULL) {
|
||||||
|
goto finally;
|
||||||
|
}
|
||||||
|
if (Py_TYPE(new) != (PyTypeObject*)metaclass) {
|
||||||
|
PyErr_SetString(PyExc_AssertionError,
|
||||||
|
"Metaclass not set properly!");
|
||||||
|
goto finally;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Assert that __subclasses__ is updated */
|
||||||
|
subclasses = PyObject_CallMethod(class, "__subclasses__", "");
|
||||||
|
if (!subclasses) {
|
||||||
|
goto finally;
|
||||||
|
}
|
||||||
|
r = PySequence_Contains(subclasses, new);
|
||||||
|
if (r < 0) {
|
||||||
|
goto finally;
|
||||||
|
}
|
||||||
|
if (r == 0) {
|
||||||
|
PyErr_SetString(PyExc_AssertionError,
|
||||||
|
"subclasses not set properly!");
|
||||||
|
goto finally;
|
||||||
|
}
|
||||||
|
|
||||||
|
result = Py_NewRef(Py_None);
|
||||||
|
|
||||||
|
finally:
|
||||||
|
Py_XDECREF(metaclass);
|
||||||
|
Py_XDECREF(class);
|
||||||
|
Py_XDECREF(new);
|
||||||
|
Py_XDECREF(subclasses);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static PyObject *
|
||||||
|
test_from_spec_invalid_metatype_inheritance(PyObject *self, PyObject *Py_UNUSED(ignored))
|
||||||
|
{
|
||||||
|
PyObject *metaclass_a = NULL;
|
||||||
|
PyObject *metaclass_b = NULL;
|
||||||
|
PyObject *class_a = NULL;
|
||||||
|
PyObject *class_b = NULL;
|
||||||
|
PyObject *bases = NULL;
|
||||||
|
PyObject *new = NULL;
|
||||||
|
PyObject *meta_error_string = NULL;
|
||||||
|
PyObject *exc_type = NULL;
|
||||||
|
PyObject *exc_value = NULL;
|
||||||
|
PyObject *exc_traceback = NULL;
|
||||||
|
PyObject *result = NULL;
|
||||||
|
|
||||||
|
metaclass_a = PyType_FromSpecWithBases(&MinimalMetaclass_spec, (PyObject*)&PyType_Type);
|
||||||
|
if (metaclass_a == NULL) {
|
||||||
|
goto finally;
|
||||||
|
}
|
||||||
|
metaclass_b = PyType_FromSpecWithBases(&MinimalMetaclass_spec, (PyObject*)&PyType_Type);
|
||||||
|
if (metaclass_b == NULL) {
|
||||||
|
goto finally;
|
||||||
|
}
|
||||||
|
class_a = PyObject_CallFunction(metaclass_a, "s(){}", "TestClassA");
|
||||||
|
if (class_a == NULL) {
|
||||||
|
goto finally;
|
||||||
|
}
|
||||||
|
|
||||||
|
class_b = PyObject_CallFunction(metaclass_b, "s(){}", "TestClassB");
|
||||||
|
if (class_b == NULL) {
|
||||||
|
goto finally;
|
||||||
|
}
|
||||||
|
|
||||||
|
bases = PyTuple_Pack(2, class_a, class_b);
|
||||||
|
if (bases == NULL) {
|
||||||
|
goto finally;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The following should raise a TypeError due to a MetaClass conflict.
|
||||||
|
*/
|
||||||
|
new = PyType_FromSpecWithBases(&MinimalType_spec, bases);
|
||||||
|
if (new != NULL) {
|
||||||
|
PyErr_SetString(PyExc_AssertionError,
|
||||||
|
"MetaType conflict not recognized by PyType_FromSpecWithBases");
|
||||||
|
goto finally;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Assert that the correct exception was raised
|
||||||
|
if (PyErr_ExceptionMatches(PyExc_TypeError)) {
|
||||||
|
PyErr_Fetch(&exc_type, &exc_value, &exc_traceback);
|
||||||
|
|
||||||
|
meta_error_string = PyUnicode_FromString("metaclass conflict:");
|
||||||
|
if (meta_error_string == NULL) {
|
||||||
|
goto finally;
|
||||||
|
}
|
||||||
|
int res = PyUnicode_Contains(exc_value, meta_error_string);
|
||||||
|
if (res < 0) {
|
||||||
|
goto finally;
|
||||||
|
}
|
||||||
|
if (res == 0) {
|
||||||
|
PyErr_SetString(PyExc_AssertionError,
|
||||||
|
"TypeError did not inlclude expected message.");
|
||||||
|
goto finally;
|
||||||
|
}
|
||||||
|
result = Py_NewRef(Py_None);
|
||||||
|
}
|
||||||
|
finally:
|
||||||
|
Py_XDECREF(metaclass_a);
|
||||||
|
Py_XDECREF(metaclass_b);
|
||||||
|
Py_XDECREF(bases);
|
||||||
|
Py_XDECREF(new);
|
||||||
|
Py_XDECREF(meta_error_string);
|
||||||
|
Py_XDECREF(exc_type);
|
||||||
|
Py_XDECREF(exc_value);
|
||||||
|
Py_XDECREF(exc_traceback);
|
||||||
|
Py_XDECREF(class_a);
|
||||||
|
Py_XDECREF(class_b);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
static PyObject *
|
static PyObject *
|
||||||
simple_str(PyObject *self) {
|
simple_str(PyObject *self) {
|
||||||
return PyUnicode_FromString("<test>");
|
return PyUnicode_FromString("<test>");
|
||||||
|
@ -5952,6 +6107,11 @@ static PyMethodDef TestMethods[] = {
|
||||||
{"test_get_type_name", test_get_type_name, METH_NOARGS},
|
{"test_get_type_name", test_get_type_name, METH_NOARGS},
|
||||||
{"test_get_type_qualname", test_get_type_qualname, METH_NOARGS},
|
{"test_get_type_qualname", test_get_type_qualname, METH_NOARGS},
|
||||||
{"test_type_from_ephemeral_spec", test_type_from_ephemeral_spec, METH_NOARGS},
|
{"test_type_from_ephemeral_spec", test_type_from_ephemeral_spec, METH_NOARGS},
|
||||||
|
{"test_from_spec_metatype_inheritance", test_from_spec_metatype_inheritance,
|
||||||
|
METH_NOARGS},
|
||||||
|
{"test_from_spec_invalid_metatype_inheritance",
|
||||||
|
test_from_spec_invalid_metatype_inheritance,
|
||||||
|
METH_NOARGS},
|
||||||
{"get_kwargs", _PyCFunction_CAST(get_kwargs),
|
{"get_kwargs", _PyCFunction_CAST(get_kwargs),
|
||||||
METH_VARARGS|METH_KEYWORDS},
|
METH_VARARGS|METH_KEYWORDS},
|
||||||
{"getargs_tuple", getargs_tuple, METH_VARARGS},
|
{"getargs_tuple", getargs_tuple, METH_VARARGS},
|
||||||
|
|
|
@ -3361,13 +3361,51 @@ static const PySlot_Offset pyslot_offsets[] = {
|
||||||
#include "typeslots.inc"
|
#include "typeslots.inc"
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/* Given a PyType_FromMetaclass `bases` argument (NULL, type, or tuple of
|
||||||
|
* types), return a tuple of types.
|
||||||
|
*/
|
||||||
|
inline static PyObject *
|
||||||
|
get_bases_tuple(PyObject *bases_in, PyType_Spec *spec)
|
||||||
|
{
|
||||||
|
if (!bases_in) {
|
||||||
|
/* Default: look in the spec, fall back to (type,). */
|
||||||
|
PyTypeObject *base = &PyBaseObject_Type; // borrowed ref
|
||||||
|
PyObject *bases = NULL; // borrowed ref
|
||||||
|
const PyType_Slot *slot;
|
||||||
|
for (slot = spec->slots; slot->slot; slot++) {
|
||||||
|
switch (slot->slot) {
|
||||||
|
case Py_tp_base:
|
||||||
|
base = slot->pfunc;
|
||||||
|
break;
|
||||||
|
case Py_tp_bases:
|
||||||
|
bases = slot->pfunc;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!bases) {
|
||||||
|
return PyTuple_Pack(1, base);
|
||||||
|
}
|
||||||
|
if (PyTuple_Check(bases)) {
|
||||||
|
return Py_NewRef(bases);
|
||||||
|
}
|
||||||
|
PyErr_SetString(PyExc_SystemError, "Py_tp_bases is not a tuple");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
if (PyTuple_Check(bases_in)) {
|
||||||
|
return Py_NewRef(bases_in);
|
||||||
|
}
|
||||||
|
// Not a tuple, should be a single type
|
||||||
|
return PyTuple_Pack(1, bases_in);
|
||||||
|
}
|
||||||
|
|
||||||
PyObject *
|
PyObject *
|
||||||
PyType_FromMetaclass(PyTypeObject *metaclass, PyObject *module,
|
PyType_FromMetaclass(PyTypeObject *metaclass, PyObject *module,
|
||||||
PyType_Spec *spec, PyObject *bases)
|
PyType_Spec *spec, PyObject *bases_in)
|
||||||
{
|
{
|
||||||
PyHeapTypeObject *res;
|
PyHeapTypeObject *res = NULL;
|
||||||
PyObject *modname;
|
PyObject *modname = NULL;
|
||||||
PyTypeObject *type, *base;
|
PyTypeObject *type;
|
||||||
|
PyObject *bases = NULL;
|
||||||
int r;
|
int r;
|
||||||
|
|
||||||
const PyType_Slot *slot;
|
const PyType_Slot *slot;
|
||||||
|
@ -3375,16 +3413,6 @@ PyType_FromMetaclass(PyTypeObject *metaclass, PyObject *module,
|
||||||
char *res_start;
|
char *res_start;
|
||||||
short slot_offset, subslot_offset;
|
short slot_offset, subslot_offset;
|
||||||
|
|
||||||
if (!metaclass) {
|
|
||||||
metaclass = &PyType_Type;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (metaclass->tp_new != PyType_Type.tp_new) {
|
|
||||||
PyErr_SetString(PyExc_TypeError,
|
|
||||||
"Metaclasses with custom tp_new are not supported.");
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
nmembers = weaklistoffset = dictoffset = vectorcalloffset = 0;
|
nmembers = weaklistoffset = dictoffset = vectorcalloffset = 0;
|
||||||
for (slot = spec->slots; slot->slot; slot++) {
|
for (slot = spec->slots; slot->slot; slot++) {
|
||||||
if (slot->slot == Py_tp_members) {
|
if (slot->slot == Py_tp_members) {
|
||||||
|
@ -3413,17 +3441,58 @@ PyType_FromMetaclass(PyTypeObject *metaclass, PyObject *module,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
res = (PyHeapTypeObject*)metaclass->tp_alloc(metaclass, nmembers);
|
|
||||||
if (res == NULL)
|
|
||||||
return NULL;
|
|
||||||
res_start = (char*)res;
|
|
||||||
|
|
||||||
if (spec->name == NULL) {
|
if (spec->name == NULL) {
|
||||||
PyErr_SetString(PyExc_SystemError,
|
PyErr_SetString(PyExc_SystemError,
|
||||||
"Type spec does not define the name field.");
|
"Type spec does not define the name field.");
|
||||||
goto fail;
|
goto finally;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Get a tuple of bases.
|
||||||
|
* bases is a strong reference (unlike bases_in).
|
||||||
|
*/
|
||||||
|
bases = get_bases_tuple(bases_in, spec);
|
||||||
|
if (!bases) {
|
||||||
|
goto finally;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Calculate the metaclass */
|
||||||
|
|
||||||
|
if (!metaclass) {
|
||||||
|
metaclass = &PyType_Type;
|
||||||
|
}
|
||||||
|
metaclass = _PyType_CalculateMetaclass(metaclass, bases);
|
||||||
|
if (metaclass == NULL) {
|
||||||
|
goto finally;
|
||||||
|
}
|
||||||
|
if (!PyType_Check(metaclass)) {
|
||||||
|
PyErr_Format(PyExc_TypeError,
|
||||||
|
"Metaclass '%R' is not a subclass of 'type'.",
|
||||||
|
metaclass);
|
||||||
|
goto finally;
|
||||||
|
}
|
||||||
|
if (metaclass->tp_new != PyType_Type.tp_new) {
|
||||||
|
PyErr_SetString(PyExc_TypeError,
|
||||||
|
"Metaclasses with custom tp_new are not supported.");
|
||||||
|
goto finally;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Calculate best base, and check that all bases are type objects */
|
||||||
|
PyTypeObject *base = best_base(bases); // borrowed ref
|
||||||
|
if (base == NULL) {
|
||||||
|
goto finally;
|
||||||
|
}
|
||||||
|
// best_base should check Py_TPFLAGS_BASETYPE & raise a proper exception,
|
||||||
|
// here we just check its work
|
||||||
|
assert(_PyType_HasFeature(base, Py_TPFLAGS_BASETYPE));
|
||||||
|
|
||||||
|
/* Allocate the new type */
|
||||||
|
|
||||||
|
res = (PyHeapTypeObject*)metaclass->tp_alloc(metaclass, nmembers);
|
||||||
|
if (res == NULL) {
|
||||||
|
goto finally;
|
||||||
|
}
|
||||||
|
res_start = (char*)res;
|
||||||
|
|
||||||
type = &res->ht_type;
|
type = &res->ht_type;
|
||||||
/* The flags must be initialized early, before the GC traverses us */
|
/* The flags must be initialized early, before the GC traverses us */
|
||||||
type->tp_flags = spec->flags | Py_TPFLAGS_HEAPTYPE;
|
type->tp_flags = spec->flags | Py_TPFLAGS_HEAPTYPE;
|
||||||
|
@ -3439,7 +3508,7 @@ PyType_FromMetaclass(PyTypeObject *metaclass, PyObject *module,
|
||||||
|
|
||||||
res->ht_name = PyUnicode_FromString(s);
|
res->ht_name = PyUnicode_FromString(s);
|
||||||
if (!res->ht_name) {
|
if (!res->ht_name) {
|
||||||
goto fail;
|
goto finally;
|
||||||
}
|
}
|
||||||
res->ht_qualname = Py_NewRef(res->ht_name);
|
res->ht_qualname = Py_NewRef(res->ht_name);
|
||||||
|
|
||||||
|
@ -3455,70 +3524,25 @@ PyType_FromMetaclass(PyTypeObject *metaclass, PyObject *module,
|
||||||
Py_ssize_t name_buf_len = strlen(spec->name) + 1;
|
Py_ssize_t name_buf_len = strlen(spec->name) + 1;
|
||||||
res->_ht_tpname = PyMem_Malloc(name_buf_len);
|
res->_ht_tpname = PyMem_Malloc(name_buf_len);
|
||||||
if (res->_ht_tpname == NULL) {
|
if (res->_ht_tpname == NULL) {
|
||||||
goto fail;
|
goto finally;
|
||||||
}
|
}
|
||||||
type->tp_name = memcpy(res->_ht_tpname, spec->name, name_buf_len);
|
type->tp_name = memcpy(res->_ht_tpname, spec->name, name_buf_len);
|
||||||
|
|
||||||
res->ht_module = Py_XNewRef(module);
|
res->ht_module = Py_XNewRef(module);
|
||||||
|
|
||||||
/* Adjust for empty tuple bases */
|
|
||||||
if (!bases) {
|
|
||||||
base = &PyBaseObject_Type;
|
|
||||||
/* See whether Py_tp_base(s) was specified */
|
|
||||||
for (slot = spec->slots; slot->slot; slot++) {
|
|
||||||
if (slot->slot == Py_tp_base)
|
|
||||||
base = slot->pfunc;
|
|
||||||
else if (slot->slot == Py_tp_bases) {
|
|
||||||
bases = slot->pfunc;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!bases) {
|
|
||||||
bases = PyTuple_Pack(1, base);
|
|
||||||
if (!bases)
|
|
||||||
goto fail;
|
|
||||||
}
|
|
||||||
else if (!PyTuple_Check(bases)) {
|
|
||||||
PyErr_SetString(PyExc_SystemError, "Py_tp_bases is not a tuple");
|
|
||||||
goto fail;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
Py_INCREF(bases);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (!PyTuple_Check(bases)) {
|
|
||||||
bases = PyTuple_Pack(1, bases);
|
|
||||||
if (!bases)
|
|
||||||
goto fail;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
Py_INCREF(bases);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Calculate best base, and check that all bases are type objects */
|
|
||||||
base = best_base(bases);
|
|
||||||
if (base == NULL) {
|
|
||||||
Py_DECREF(bases);
|
|
||||||
goto fail;
|
|
||||||
}
|
|
||||||
if (!_PyType_HasFeature(base, Py_TPFLAGS_BASETYPE)) {
|
|
||||||
PyErr_Format(PyExc_TypeError,
|
|
||||||
"type '%.100s' is not an acceptable base type",
|
|
||||||
base->tp_name);
|
|
||||||
Py_DECREF(bases);
|
|
||||||
goto fail;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Initialize essential fields */
|
/* Initialize essential fields */
|
||||||
type->tp_as_async = &res->as_async;
|
type->tp_as_async = &res->as_async;
|
||||||
type->tp_as_number = &res->as_number;
|
type->tp_as_number = &res->as_number;
|
||||||
type->tp_as_sequence = &res->as_sequence;
|
type->tp_as_sequence = &res->as_sequence;
|
||||||
type->tp_as_mapping = &res->as_mapping;
|
type->tp_as_mapping = &res->as_mapping;
|
||||||
type->tp_as_buffer = &res->as_buffer;
|
type->tp_as_buffer = &res->as_buffer;
|
||||||
/* Set tp_base and tp_bases */
|
|
||||||
type->tp_bases = bases;
|
|
||||||
Py_INCREF(base);
|
|
||||||
type->tp_base = base;
|
|
||||||
|
|
||||||
|
/* Set tp_base and tp_bases */
|
||||||
|
type->tp_base = (PyTypeObject *)Py_NewRef(base);
|
||||||
|
type->tp_bases = bases;
|
||||||
|
bases = NULL; // We give our reference to bases to the type
|
||||||
|
|
||||||
|
/* Copy the sizes */
|
||||||
type->tp_basicsize = spec->basicsize;
|
type->tp_basicsize = spec->basicsize;
|
||||||
type->tp_itemsize = spec->itemsize;
|
type->tp_itemsize = spec->itemsize;
|
||||||
|
|
||||||
|
@ -3526,7 +3550,7 @@ PyType_FromMetaclass(PyTypeObject *metaclass, PyObject *module,
|
||||||
if (slot->slot < 0
|
if (slot->slot < 0
|
||||||
|| (size_t)slot->slot >= Py_ARRAY_LENGTH(pyslot_offsets)) {
|
|| (size_t)slot->slot >= Py_ARRAY_LENGTH(pyslot_offsets)) {
|
||||||
PyErr_SetString(PyExc_RuntimeError, "invalid slot offset");
|
PyErr_SetString(PyExc_RuntimeError, "invalid slot offset");
|
||||||
goto fail;
|
goto finally;
|
||||||
}
|
}
|
||||||
else if (slot->slot == Py_tp_base || slot->slot == Py_tp_bases) {
|
else if (slot->slot == Py_tp_base || slot->slot == Py_tp_bases) {
|
||||||
/* Processed above */
|
/* Processed above */
|
||||||
|
@ -3544,7 +3568,7 @@ PyType_FromMetaclass(PyTypeObject *metaclass, PyObject *module,
|
||||||
if (tp_doc == NULL) {
|
if (tp_doc == NULL) {
|
||||||
type->tp_doc = NULL;
|
type->tp_doc = NULL;
|
||||||
PyErr_NoMemory();
|
PyErr_NoMemory();
|
||||||
goto fail;
|
goto finally;
|
||||||
}
|
}
|
||||||
memcpy(tp_doc, slot->pfunc, len);
|
memcpy(tp_doc, slot->pfunc, len);
|
||||||
type->tp_doc = tp_doc;
|
type->tp_doc = tp_doc;
|
||||||
|
@ -3579,8 +3603,9 @@ PyType_FromMetaclass(PyTypeObject *metaclass, PyObject *module,
|
||||||
type->tp_vectorcall_offset = vectorcalloffset;
|
type->tp_vectorcall_offset = vectorcalloffset;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (PyType_Ready(type) < 0)
|
if (PyType_Ready(type) < 0) {
|
||||||
goto fail;
|
goto finally;
|
||||||
|
}
|
||||||
|
|
||||||
if (type->tp_flags & Py_TPFLAGS_MANAGED_DICT) {
|
if (type->tp_flags & Py_TPFLAGS_MANAGED_DICT) {
|
||||||
res->ht_cached_keys = _PyDict_NewKeysForClass();
|
res->ht_cached_keys = _PyDict_NewKeysForClass();
|
||||||
|
@ -3588,29 +3613,33 @@ PyType_FromMetaclass(PyTypeObject *metaclass, PyObject *module,
|
||||||
|
|
||||||
if (type->tp_doc) {
|
if (type->tp_doc) {
|
||||||
PyObject *__doc__ = PyUnicode_FromString(_PyType_DocWithoutSignature(type->tp_name, type->tp_doc));
|
PyObject *__doc__ = PyUnicode_FromString(_PyType_DocWithoutSignature(type->tp_name, type->tp_doc));
|
||||||
if (!__doc__)
|
if (!__doc__) {
|
||||||
goto fail;
|
goto finally;
|
||||||
|
}
|
||||||
r = PyDict_SetItem(type->tp_dict, &_Py_ID(__doc__), __doc__);
|
r = PyDict_SetItem(type->tp_dict, &_Py_ID(__doc__), __doc__);
|
||||||
Py_DECREF(__doc__);
|
Py_DECREF(__doc__);
|
||||||
if (r < 0)
|
if (r < 0) {
|
||||||
goto fail;
|
goto finally;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (weaklistoffset) {
|
if (weaklistoffset) {
|
||||||
type->tp_weaklistoffset = weaklistoffset;
|
type->tp_weaklistoffset = weaklistoffset;
|
||||||
if (PyDict_DelItemString((PyObject *)type->tp_dict, "__weaklistoffset__") < 0)
|
if (PyDict_DelItemString((PyObject *)type->tp_dict, "__weaklistoffset__") < 0) {
|
||||||
goto fail;
|
goto finally;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (dictoffset) {
|
if (dictoffset) {
|
||||||
type->tp_dictoffset = dictoffset;
|
type->tp_dictoffset = dictoffset;
|
||||||
if (PyDict_DelItemString((PyObject *)type->tp_dict, "__dictoffset__") < 0)
|
if (PyDict_DelItemString((PyObject *)type->tp_dict, "__dictoffset__") < 0) {
|
||||||
goto fail;
|
goto finally;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Set type.__module__ */
|
/* Set type.__module__ */
|
||||||
r = PyDict_Contains(type->tp_dict, &_Py_ID(__module__));
|
r = PyDict_Contains(type->tp_dict, &_Py_ID(__module__));
|
||||||
if (r < 0) {
|
if (r < 0) {
|
||||||
goto fail;
|
goto finally;
|
||||||
}
|
}
|
||||||
if (r == 0) {
|
if (r == 0) {
|
||||||
s = strrchr(spec->name, '.');
|
s = strrchr(spec->name, '.');
|
||||||
|
@ -3618,26 +3647,30 @@ PyType_FromMetaclass(PyTypeObject *metaclass, PyObject *module,
|
||||||
modname = PyUnicode_FromStringAndSize(
|
modname = PyUnicode_FromStringAndSize(
|
||||||
spec->name, (Py_ssize_t)(s - spec->name));
|
spec->name, (Py_ssize_t)(s - spec->name));
|
||||||
if (modname == NULL) {
|
if (modname == NULL) {
|
||||||
goto fail;
|
goto finally;
|
||||||
}
|
}
|
||||||
r = PyDict_SetItem(type->tp_dict, &_Py_ID(__module__), modname);
|
r = PyDict_SetItem(type->tp_dict, &_Py_ID(__module__), modname);
|
||||||
Py_DECREF(modname);
|
if (r != 0) {
|
||||||
if (r != 0)
|
goto finally;
|
||||||
goto fail;
|
}
|
||||||
} else {
|
}
|
||||||
|
else {
|
||||||
if (PyErr_WarnFormat(PyExc_DeprecationWarning, 1,
|
if (PyErr_WarnFormat(PyExc_DeprecationWarning, 1,
|
||||||
"builtin type %.200s has no __module__ attribute",
|
"builtin type %.200s has no __module__ attribute",
|
||||||
spec->name))
|
spec->name))
|
||||||
goto fail;
|
goto finally;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
assert(_PyType_CheckConsistency(type));
|
assert(_PyType_CheckConsistency(type));
|
||||||
return (PyObject*)res;
|
|
||||||
|
|
||||||
fail:
|
finally:
|
||||||
Py_DECREF(res);
|
if (PyErr_Occurred()) {
|
||||||
return NULL;
|
Py_CLEAR(res);
|
||||||
|
}
|
||||||
|
Py_XDECREF(bases);
|
||||||
|
Py_XDECREF(modname);
|
||||||
|
return (PyObject*)res;
|
||||||
}
|
}
|
||||||
|
|
||||||
PyObject *
|
PyObject *
|
||||||
|
|
Loading…
Reference in New Issue