diff --git a/Doc/c-api/type.rst b/Doc/c-api/type.rst index 6f9c7a84a17..aa5eef04033 100644 --- a/Doc/c-api/type.rst +++ b/Doc/c-api/type.rst @@ -85,3 +85,15 @@ Type Objects their initialization. This function is responsible for adding inherited slots from a type's base class. Return ``0`` on success, or return ``-1`` and sets an exception on error. + +.. c:function:: PyObject* PyType_FromSpec(PyType_Spec *spec) + + Creates and returns a heap type object from the *spec* passed to the function. + +.. c:function:: PyObject* PyType_FromSpecWithBases(PyType_Spec *spec, PyObject *bases) + + Creates and returns a heap type object from the *spec*. In addition to that, + the created heap type contains all types contained by the *bases* tuple as base + types. This allows the caller to reference other heap types as base types. + + .. versionadded:: 3.3 diff --git a/Include/object.h b/Include/object.h index 68001e79f48..709a9fff138 100644 --- a/Include/object.h +++ b/Include/object.h @@ -433,6 +433,9 @@ typedef struct{ } PyType_Spec; PyAPI_FUNC(PyObject*) PyType_FromSpec(PyType_Spec*); +#if !defined(Py_LIMITED_API) || Py_LIMITED_API+0 >= 0x03030000 +PyAPI_FUNC(PyObject*) PyType_FromSpecWithBases(PyType_Spec*, PyObject*); +#endif #ifndef Py_LIMITED_API /* The *real* layout of a type object when allocated on the heap */ diff --git a/Misc/NEWS b/Misc/NEWS index 35e8ede28dc..623a5a493fb 100644 --- a/Misc/NEWS +++ b/Misc/NEWS @@ -10,6 +10,8 @@ What's New in Python 3.3.0 Beta 1? Core and Builtins ----------------- +- Issue #15146: Add PyType_FromSpecWithBases. Patch by Robin Schreiber. + - Issue #15142: Fix reference leak when deallocating instances of types created using PyType_FromSpec(). diff --git a/Objects/typeobject.c b/Objects/typeobject.c index d8bdeaf5d7e..a2b1619595d 100644 --- a/Objects/typeobject.c +++ b/Objects/typeobject.c @@ -48,6 +48,9 @@ _Py_IDENTIFIER(__new__); static PyObject * _PyType_LookupId(PyTypeObject *type, struct _Py_Identifier *name); +static PyObject * +slot_tp_new(PyTypeObject *type, PyObject *args, PyObject *kwds); + unsigned int PyType_ClearCache(void) { @@ -2375,22 +2378,75 @@ static short slotoffsets[] = { }; PyObject * -PyType_FromSpec(PyType_Spec *spec) +PyType_FromSpecWithBases(PyType_Spec *spec, PyObject *bases) { PyHeapTypeObject *res = (PyHeapTypeObject*)PyType_GenericAlloc(&PyType_Type, 0); + PyTypeObject *type, *base; + char *s; char *res_start = (char*)res; PyType_Slot *slot; + + /* Set the type name and qualname */ + s = strrchr(spec->name, '.'); + if (s == NULL) + s = (char*)spec->name; + else + s++; if (res == NULL) return NULL; - res->ht_name = PyUnicode_FromString(spec->name); + res->ht_name = PyUnicode_FromString(s); if (!res->ht_name) goto fail; res->ht_qualname = res->ht_name; Py_INCREF(res->ht_qualname); - res->ht_type.tp_name = _PyUnicode_AsString(res->ht_name); + res->ht_type.tp_name = spec->name; if (!res->ht_type.tp_name) goto fail; + + /* 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; + Py_INCREF(bases); + } + } + if (!bases) + bases = PyTuple_Pack(1, base); + 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) { + goto fail; + } + if (!PyType_HasFeature(base, Py_TPFLAGS_BASETYPE)) { + PyErr_Format(PyExc_TypeError, + "type '%.100s' is not an acceptable base type", + base->tp_name); + goto fail; + } + + type = (PyTypeObject *)res; + /* Initialize essential fields */ + type->tp_as_number = &res->as_number; + type->tp_as_sequence = &res->as_sequence; + type->tp_as_mapping = &res->as_mapping; + type->tp_as_buffer = &res->as_buffer; + /* Set tp_base and tp_bases */ + type->tp_bases = bases; + bases = NULL; + Py_INCREF(base); + type->tp_base = base; res->ht_type.tp_basicsize = spec->basicsize; res->ht_type.tp_itemsize = spec->itemsize; @@ -2401,6 +2457,9 @@ PyType_FromSpec(PyType_Spec *spec) PyErr_SetString(PyExc_RuntimeError, "invalid slot offset"); goto fail; } + if (slot->slot == Py_tp_base || slot->slot == Py_tp_bases) + /* Processed above */ + continue; *(void**)(res_start + slotoffsets[slot->slot]) = slot->pfunc; /* need to make a copy of the docstring slot, which usually @@ -2427,6 +2486,13 @@ PyType_FromSpec(PyType_Spec *spec) if (PyType_Ready(&res->ht_type) < 0) goto fail; + /* Set type.__module__ */ + s = strrchr(spec->name, '.'); + if (s != NULL) + _PyDict_SetItemId(type->tp_dict, &PyId___module__, + PyUnicode_FromStringAndSize( + spec->name, (Py_ssize_t)(s - spec->name))); + return (PyObject*)res; fail: @@ -2434,6 +2500,12 @@ PyType_FromSpec(PyType_Spec *spec) return NULL; } +PyObject * +PyType_FromSpec(PyType_Spec *spec) +{ + return PyType_FromSpecWithBases(spec, NULL); +} + /* Internal API to look for a name through the MRO. This returns a borrowed reference, and doesn't set an exception! */ @@ -4763,7 +4835,7 @@ tp_new_wrapper(PyObject *self, PyObject *args, PyObject *kwds) object.__new__(dict). To do this, we check that the most derived base that's not a heap type is this type. */ staticbase = subtype; - while (staticbase && (staticbase->tp_flags & Py_TPFLAGS_HEAPTYPE)) + while (staticbase && (staticbase->tp_new == slot_tp_new)) staticbase = staticbase->tp_base; /* If staticbase is NULL now, it is a really weird type. In the spirit of backwards compatibility (?), just shut up. */ diff --git a/PC/python3.def b/PC/python3.def index 0c9e25ced10..f16afca3a52 100644 --- a/PC/python3.def +++ b/PC/python3.def @@ -1,3 +1,4 @@ +; When changing this file, run python33gen.py LIBRARY "python3" EXPORTS PyArg_Parse=python33.PyArg_Parse @@ -513,6 +514,7 @@ EXPORTS PyTuple_Type=python33.PyTuple_Type DATA PyType_ClearCache=python33.PyType_ClearCache PyType_FromSpec=python33.PyType_FromSpec + PyType_FromSpecWithBases=python33.PyType_FromSpecWithBases PyType_GenericAlloc=python33.PyType_GenericAlloc PyType_GenericNew=python33.PyType_GenericNew PyType_GetFlags=python33.PyType_GetFlags diff --git a/PC/python33stub.def b/PC/python33stub.def index 20a5868127c..4111d30b872 100644 --- a/PC/python33stub.def +++ b/PC/python33stub.def @@ -513,6 +513,7 @@ PyTuple_Size PyTuple_Type PyType_ClearCache PyType_FromSpec +PyType_FromSpecWithBases PyType_GenericAlloc PyType_GenericNew PyType_GetFlags