Skip to content

Commit 9c56409

Browse files
committed
Issue python#15146: Add PyType_FromSpecWithBases. Patch by Robin Schreiber.
1 parent 110ee34 commit 9c56409

File tree

6 files changed

+96
-4
lines changed

6 files changed

+96
-4
lines changed

Doc/c-api/type.rst

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,3 +85,15 @@ Type Objects
8585
their initialization. This function is responsible for adding inherited slots
8686
from a type's base class. Return ``0`` on success, or return ``-1`` and sets an
8787
exception on error.
88+
89+
.. c:function:: PyObject* PyType_FromSpec(PyType_Spec *spec)
90+
91+
Creates and returns a heap type object from the *spec* passed to the function.
92+
93+
.. c:function:: PyObject* PyType_FromSpecWithBases(PyType_Spec *spec, PyObject *bases)
94+
95+
Creates and returns a heap type object from the *spec*. In addition to that,
96+
the created heap type contains all types contained by the *bases* tuple as base
97+
types. This allows the caller to reference other heap types as base types.
98+
99+
.. versionadded:: 3.3

Include/object.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -433,6 +433,9 @@ typedef struct{
433433
} PyType_Spec;
434434

435435
PyAPI_FUNC(PyObject*) PyType_FromSpec(PyType_Spec*);
436+
#if !defined(Py_LIMITED_API) || Py_LIMITED_API+0 >= 0x03030000
437+
PyAPI_FUNC(PyObject*) PyType_FromSpecWithBases(PyType_Spec*, PyObject*);
438+
#endif
436439

437440
#ifndef Py_LIMITED_API
438441
/* The *real* layout of a type object when allocated on the heap */

Misc/NEWS

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ What's New in Python 3.3.0 Beta 1?
1010
Core and Builtins
1111
-----------------
1212

13+
- Issue #15146: Add PyType_FromSpecWithBases. Patch by Robin Schreiber.
14+
1315
- Issue #15142: Fix reference leak when deallocating instances of types
1416
created using PyType_FromSpec().
1517

Objects/typeobject.c

Lines changed: 76 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,9 @@ _Py_IDENTIFIER(__new__);
4848
static PyObject *
4949
_PyType_LookupId(PyTypeObject *type, struct _Py_Identifier *name);
5050

51+
static PyObject *
52+
slot_tp_new(PyTypeObject *type, PyObject *args, PyObject *kwds);
53+
5154
unsigned int
5255
PyType_ClearCache(void)
5356
{
@@ -2375,22 +2378,75 @@ static short slotoffsets[] = {
23752378
};
23762379

23772380
PyObject *
2378-
PyType_FromSpec(PyType_Spec *spec)
2381+
PyType_FromSpecWithBases(PyType_Spec *spec, PyObject *bases)
23792382
{
23802383
PyHeapTypeObject *res = (PyHeapTypeObject*)PyType_GenericAlloc(&PyType_Type, 0);
2384+
PyTypeObject *type, *base;
2385+
char *s;
23812386
char *res_start = (char*)res;
23822387
PyType_Slot *slot;
2388+
2389+
/* Set the type name and qualname */
2390+
s = strrchr(spec->name, '.');
2391+
if (s == NULL)
2392+
s = (char*)spec->name;
2393+
else
2394+
s++;
23832395

23842396
if (res == NULL)
23852397
return NULL;
2386-
res->ht_name = PyUnicode_FromString(spec->name);
2398+
res->ht_name = PyUnicode_FromString(s);
23872399
if (!res->ht_name)
23882400
goto fail;
23892401
res->ht_qualname = res->ht_name;
23902402
Py_INCREF(res->ht_qualname);
2391-
res->ht_type.tp_name = _PyUnicode_AsString(res->ht_name);
2403+
res->ht_type.tp_name = spec->name;
23922404
if (!res->ht_type.tp_name)
23932405
goto fail;
2406+
2407+
/* Adjust for empty tuple bases */
2408+
if (!bases) {
2409+
base = &PyBaseObject_Type;
2410+
/* See whether Py_tp_base(s) was specified */
2411+
for (slot = spec->slots; slot->slot; slot++) {
2412+
if (slot->slot == Py_tp_base)
2413+
base = slot->pfunc;
2414+
else if (slot->slot == Py_tp_bases) {
2415+
bases = slot->pfunc;
2416+
Py_INCREF(bases);
2417+
}
2418+
}
2419+
if (!bases)
2420+
bases = PyTuple_Pack(1, base);
2421+
if (!bases)
2422+
goto fail;
2423+
}
2424+
else
2425+
Py_INCREF(bases);
2426+
2427+
/* Calculate best base, and check that all bases are type objects */
2428+
base = best_base(bases);
2429+
if (base == NULL) {
2430+
goto fail;
2431+
}
2432+
if (!PyType_HasFeature(base, Py_TPFLAGS_BASETYPE)) {
2433+
PyErr_Format(PyExc_TypeError,
2434+
"type '%.100s' is not an acceptable base type",
2435+
base->tp_name);
2436+
goto fail;
2437+
}
2438+
2439+
type = (PyTypeObject *)res;
2440+
/* Initialize essential fields */
2441+
type->tp_as_number = &res->as_number;
2442+
type->tp_as_sequence = &res->as_sequence;
2443+
type->tp_as_mapping = &res->as_mapping;
2444+
type->tp_as_buffer = &res->as_buffer;
2445+
/* Set tp_base and tp_bases */
2446+
type->tp_bases = bases;
2447+
bases = NULL;
2448+
Py_INCREF(base);
2449+
type->tp_base = base;
23942450

23952451
res->ht_type.tp_basicsize = spec->basicsize;
23962452
res->ht_type.tp_itemsize = spec->itemsize;
@@ -2401,6 +2457,9 @@ PyType_FromSpec(PyType_Spec *spec)
24012457
PyErr_SetString(PyExc_RuntimeError, "invalid slot offset");
24022458
goto fail;
24032459
}
2460+
if (slot->slot == Py_tp_base || slot->slot == Py_tp_bases)
2461+
/* Processed above */
2462+
continue;
24042463
*(void**)(res_start + slotoffsets[slot->slot]) = slot->pfunc;
24052464

24062465
/* need to make a copy of the docstring slot, which usually
@@ -2427,13 +2486,26 @@ PyType_FromSpec(PyType_Spec *spec)
24272486
if (PyType_Ready(&res->ht_type) < 0)
24282487
goto fail;
24292488

2489+
/* Set type.__module__ */
2490+
s = strrchr(spec->name, '.');
2491+
if (s != NULL)
2492+
_PyDict_SetItemId(type->tp_dict, &PyId___module__,
2493+
PyUnicode_FromStringAndSize(
2494+
spec->name, (Py_ssize_t)(s - spec->name)));
2495+
24302496
return (PyObject*)res;
24312497

24322498
fail:
24332499
Py_DECREF(res);
24342500
return NULL;
24352501
}
24362502

2503+
PyObject *
2504+
PyType_FromSpec(PyType_Spec *spec)
2505+
{
2506+
return PyType_FromSpecWithBases(spec, NULL);
2507+
}
2508+
24372509

24382510
/* Internal API to look for a name through the MRO.
24392511
This returns a borrowed reference, and doesn't set an exception! */
@@ -4763,7 +4835,7 @@ tp_new_wrapper(PyObject *self, PyObject *args, PyObject *kwds)
47634835
object.__new__(dict). To do this, we check that the
47644836
most derived base that's not a heap type is this type. */
47654837
staticbase = subtype;
4766-
while (staticbase && (staticbase->tp_flags & Py_TPFLAGS_HEAPTYPE))
4838+
while (staticbase && (staticbase->tp_new == slot_tp_new))
47674839
staticbase = staticbase->tp_base;
47684840
/* If staticbase is NULL now, it is a really weird type.
47694841
In the spirit of backwards compatibility (?), just shut up. */

PC/python3.def

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
; When changing this file, run python33gen.py
12
LIBRARY "python3"
23
EXPORTS
34
PyArg_Parse=python33.PyArg_Parse
@@ -513,6 +514,7 @@ EXPORTS
513514
PyTuple_Type=python33.PyTuple_Type DATA
514515
PyType_ClearCache=python33.PyType_ClearCache
515516
PyType_FromSpec=python33.PyType_FromSpec
517+
PyType_FromSpecWithBases=python33.PyType_FromSpecWithBases
516518
PyType_GenericAlloc=python33.PyType_GenericAlloc
517519
PyType_GenericNew=python33.PyType_GenericNew
518520
PyType_GetFlags=python33.PyType_GetFlags

PC/python33stub.def

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -513,6 +513,7 @@ PyTuple_Size
513513
PyTuple_Type
514514
PyType_ClearCache
515515
PyType_FromSpec
516+
PyType_FromSpecWithBases
516517
PyType_GenericAlloc
517518
PyType_GenericNew
518519
PyType_GetFlags

0 commit comments

Comments
 (0)